[
  {
    "path": ".github/workflows/neocities.yml",
    "content": "name: Deploy docs to Neocities\non:\n  push:\n    branches: [main]\njobs:\n  deploy_to_neocities:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Clone repo\n        uses: actions/checkout@v3\n      - run: mkdir public && .github/mddocs sq.md public/sq.html\n      - name: Deploy to neocities\n        uses: bcomnes/deploy-to-neocities@v1\n        with:\n          api_token: ${{ secrets.NEOCITIES_API_KEY }}\n          dist_dir: public\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: tests\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\njobs:\n  run_sq_tests:\n    runs-on: ubuntu-latest\n    services:\n      postgres:\n        image: postgres\n        env:\n          POSTGRES_USER: 'user1'\n          POSTGRES_PASSWORD: 'Hunter2!'\n          POSTGRES_DB: 'sakila'\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n        ports:\n          - '5456:5432'\n      mysql:\n        image: mysql\n        env:\n          MYSQL_ROOT_PASSWORD: 'Hunter2!'\n          MYSQL_USER: 'user1'\n          MYSQL_PASSWORD: 'Hunter2!'\n          MYSQL_DATABASE: 'sakila'\n        options: >-\n          --health-cmd \"mysqladmin ping\"\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n          --health-start-period 30s\n        ports:\n          - '3330:3306'\n      sqlserver:\n        image: 'mcr.microsoft.com/azure-sql-edge'\n        env:\n          ACCEPT_EULA: 'Y'\n          MSSQL_SA_PASSWORD: 'Hunter2!'\n        options: >-\n          --health-cmd \"/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Hunter2! -Q 'select 1' -b -o /dev/null\"\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n          --health-start-period 30s\n        ports:\n          - '1447:1433'\n    steps:\n      - name: Install go\n        uses: actions/setup-go@v3\n        with:\n          go-version: '>=1.18.0'\n      - name: Clone repo\n        uses: actions/checkout@v3\n      - run: go test . -tags=fts5 -failfast -shuffle on -coverprofile coverage -race -postgres 'postgres://user1:Hunter2!@localhost:5456/sakila?sslmode=disable' -mysql 'root:Hunter2!@tcp(localhost:3330)/sakila?multiStatements=true&parseTime=true' -sqlserver 'sqlserver://sa:Hunter2!@localhost:1447'\n      - name: Convert coverage to coverage.lcov\n        uses: jandelgado/gcov2lcov-action@v1.0.0\n        with:\n          infile: coverage\n          outfile: coverage.lcov\n      - name: Upload coverage.lcov to Coveralls\n        uses: coverallsapp/github-action@master\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          path-to-lcov: coverage.lcov\n"
  },
  {
    "path": ".gitignore",
    "content": "*.sqlite*\n.idea\ncoverage.out\ncoverage\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Chua Bok Woon\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[![GoDoc](https://img.shields.io/badge/pkg.go.dev-sq-blue)](https://pkg.go.dev/github.com/bokwoon95/sq)\n![tests](https://github.com/bokwoon95/sq/actions/workflows/tests.yml/badge.svg?branch=main)\n[![Go Report Card](https://goreportcard.com/badge/github.com/bokwoon95/sq)](https://goreportcard.com/report/github.com/bokwoon95/sq)\n[![Coverage Status](https://coveralls.io/repos/github/bokwoon95/sq/badge.svg?branch=main)](https://coveralls.io/github/bokwoon95/sq?branch=main)\n\n<img src=\"https://raw.githubusercontent.com/bokwoon95/sq/main/header.png\" title=\"code example of a select query using sq\" alt=\"code example of a select query using sq, to give viewers a quick idea of what the library is about\" style=\"max-width:90%;\">\n\n# sq (Structured Query)\n\n[one-page documentation](https://bokwoon.neocities.org/sq.html)\n\nsq is a type-safe data mapper and query builder for Go. Its concept is simple: you provide a callback function that maps a row to a struct, generics ensure that you get back a slice of structs at the end. Additionally, mentioning a column in the callback function automatically adds it to the SELECT clause so you don't even have to explicitly mention what columns you want to select: the [act of mapping a column is the same as selecting it](#select-example-raw-sql). This eliminates a source of errors where you have specify the columns twice (once in the query itself, once to the call to rows.Scan) and end up missing a column, getting the column order wrong or mistyping a column name.\n\nNotable features:\n\n- Works across SQLite, Postgres, MySQL and SQL Server. [[more info](https://bokwoon.neocities.org/sq.html#set-query-dialect)]\n- Each dialect has its own query builder, allowing you to use dialect-specific features. [[more info](https://bokwoon.neocities.org/sq.html#dialect-specific-features)]\n- Declarative schema migrations. [[more info](https://bokwoon.neocities.org/sq.html#declarative-schema)]\n- Supports arrays, enums, JSON and UUID. [[more info](https://bokwoon.neocities.org/sq.html#arrays-enums-json-uuid)]\n- Query logging. [[more info](https://bokwoon.neocities.org/sq.html#logging)]\n\n# Installation\n\nThis package only supports Go 1.19 and above.\n\n```shell\n$ go get github.com/bokwoon95/sq\n$ go install -tags=fts5 github.com/bokwoon95/sqddl@latest\n```\n\n# Features\n\n- IN\n    - [In Slice](https://bokwoon.neocities.org/sq.html#in-slice) - `a IN (1, 2, 3)`\n    - [In RowValues](https://bokwoon.neocities.org/sq.html#in-rowvalues) - `(a, b, c) IN ((1, 2, 3), (4, 5, 6), (7, 8, 9))`\n    - [In Subquery](https://bokwoon.neocities.org/sq.html#in-subquery) - `(a, b) IN (SELECT a, b FROM tbl WHERE condition)`\n- CASE\n    - [Predicate Case](https://bokwoon.neocities.org/sq.html#predicate-case) - `CASE WHEN a THEN b WHEN c THEN d ELSE e END`\n    - [Simple case](https://bokwoon.neocities.org/sq.html#simple-case) - `CASE expr WHEN a THEN b WHEN c THEN d ELSE e END`\n- EXISTS\n    - [Where Exists](https://bokwoon.neocities.org/sq.html#where-exists)\n    - [Where Not Exists](https://bokwoon.neocities.org/sq.html#where-not-exists)\n    - [Select Exists](https://bokwoon.neocities.org/sq.html#querybuilder-fetch-exists)\n- [Subqueries](https://bokwoon.neocities.org/sq.html#subqueries)\n- [WITH (Common Table Expressions)](https://bokwoon.neocities.org/sq.html#common-table-expressions)\n- [Aggregate functions](https://bokwoon.neocities.org/sq.html#aggregate-functions)\n- [Window functions](https://bokwoon.neocities.org/sq.html#window-functions)\n- [UNION, INTERSECT, EXCEPT](https://bokwoon.neocities.org/sq.html#union-intersect-except)\n- [INSERT from SELECT](https://bokwoon.neocities.org/sq.html#querybuilder-insert-from-select)\n- RETURNING\n    - [SQLite RETURNING](https://bokwoon.neocities.org/sq.html#sqlite-returning)\n    - [Postgres RETURNING](https://bokwoon.neocities.org/sq.html#postgres-returning)\n- LastInsertId\n    - [SQLite LastInsertId](https://bokwoon.neocities.org/sq.html#sqlite-last-insert-id)\n    - [MySQL LastInsertId](https://bokwoon.neocities.org/sq.html#mysql-last-insert-id)\n- Insert ignore duplicates\n    - [SQLite Insert ignore duplicates](https://bokwoon.neocities.org/sq.html#sqlite-insert-ignore-duplicates)\n    - [Postgres Insert ignore duplicates](https://bokwoon.neocities.org/sq.html#postgres-insert-ignore-duplicates)\n    - [MySQL Insert ignore duplicates](https://bokwoon.neocities.org/sq.html#mysql-insert-ignore-duplicates)\n    - [SQL Server Insert ignore duplicates](https://bokwoon.neocities.org/sq.html#sqlserver-insert-ignore-duplicates)\n- Upsert\n    - [SQLite Upsert](https://bokwoon.neocities.org/sq.html#sqlite-upsert)\n    - [Postgres Upsert](https://bokwoon.neocities.org/sq.html#postgres-upsert)\n    - [MySQL Upsert](https://bokwoon.neocities.org/sq.html#mysql-upsert)\n    - [SQL Server Upsert](https://bokwoon.neocities.org/sq.html#sqlserver-upsert)\n- Update with Join\n    - [SQLite Update with Join](https://bokwoon.neocities.org/sq.html#sqlite-update-with-join)\n    - [Postgres Update with Join](https://bokwoon.neocities.org/sq.html#postgres-update-with-join)\n    - [MySQL Update with Join](https://bokwoon.neocities.org/sq.html#mysql-update-with-join)\n    - [SQL Server Update with Join](https://bokwoon.neocities.org/sq.html#sqlserver-update-with-join)\n- Delete with Join\n    - [SQLite Delete with Join](https://bokwoon.neocities.org/sq.html#sqlite-delete-with-join)\n    - [Postgres Delete with Join](https://bokwoon.neocities.org/sq.html#postgres-delete-with-join)\n    - [MySQL Delete with Join](https://bokwoon.neocities.org/sq.html#mysql-delete-with-join)\n    - [SQL Server Delete with Join](https://bokwoon.neocities.org/sq.html#sqlserver-delete-with-join)\n- Bulk Update\n    - [SQLite Bulk Update](https://bokwoon.neocities.org/sq.html#sqlite-bulk-update)\n    - [Postgres Bulk Update](https://bokwoon.neocities.org/sq.html#postgres-bulk-update)\n    - [MySQL Bulk Update](https://bokwoon.neocities.org/sq.html#mysql-bulk-update)\n    - [SQL Server Bulk Update](https://bokwoon.neocities.org/sq.html#sqlserver-bulk-update)\n\n## SELECT example (Raw SQL)\n\n```go\ndb, err := sql.Open(\"postgres\", \"postgres://username:password@localhost:5432/sakila?sslmode=disable\")\n\nactors, err := sq.FetchAll(db, sq.\n    Queryf(\"SELECT {*} FROM actor AS a WHERE a.actor_id IN ({})\",\n        []int{1, 2, 3, 4, 5},\n    ).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:     row.Int(\"a.actor_id\"),\n            FirstName:   row.String(\"a.first_name\"),\n            LastName:    row.String(\"a.last_name\"),\n            LastUpdate:  row.Time(\"a.last_update\"),\n        }\n    },\n)\n```\n\n## SELECT example (Query Builder)\n\nTo use the query builder, you must first [define your table structs](https://bokwoon.neocities.org/sq.html#table-structs).\n\n```go\ntype ACTOR struct {\n    sq.TableStruct\n    ACTOR_ID    sq.NumberField\n    FIRST_NAME  sq.StringField\n    LAST_NAME   sq.StringField\n    LAST_UPDATE sq.TimeField\n}\n\ndb, err := sql.Open(\"postgres\", \"postgres://username:password@localhost:5432/sakila?sslmode=disable\")\n\na := sq.New[ACTOR](\"a\")\nactors, err := sq.FetchAll(db, sq.\n    From(a).\n    Where(a.ACTOR_ID.In([]int{1, 2, 3, 4, 5})).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:     row.IntField(a.ACTOR_ID),\n            FirstName:   row.StringField(a.FIRST_NAME),\n            LastName:    row.StringField(a.LAST_NAME),\n            LastUpdate:  row.TimeField(a.LAST_UPDATE),\n        }\n    },\n)\n```\n\n## INSERT example (Raw SQL)\n\n```go\ndb, err := sql.Open(\"postgres\", \"postgres://username:password@localhost:5432/sakila?sslmode=disable\")\n\n_, err := sq.Exec(db, sq.\n    Queryf(\"INSERT INTO actor (actor_id, first_name, last_name) VALUES {}\", sq.RowValues{\n        {18, \"DAN\", \"TORN\"},\n        {56, \"DAN\", \"HARRIS\"},\n        {166, \"DAN\", \"STREEP\"},\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n## INSERT example (Query Builder)\n\nTo use the query builder, you must first [define your table structs](https://bokwoon.neocities.org/sq.html#table-structs).\n\n```go\ntype ACTOR struct {\n    sq.TableStruct\n    ACTOR_ID    sq.NumberField\n    FIRST_NAME  sq.StringField\n    LAST_NAME   sq.StringField\n    LAST_UPDATE sq.TimeField\n}\n\ndb, err := sql.Open(\"postgres\", \"postgres://username:password@localhost:5432/sakila?sslmode=disable\")\n\na := sq.New[ACTOR](\"a\")\n_, err := sq.Exec(db, sq.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Values(18, \"DAN\", \"TORN\").\n    Values(56, \"DAN\", \"HARRIS\").\n    Values(166, \"DAN\", \"STREEP\").\n    SetDialect(sq.DialectPostgres),\n)\n```\n\nFor a more detailed overview, look at the [Quickstart](https://bokwoon.neocities.org/sq.html#quickstart).\n\n## Project Status\n\nsq is done for my use case (hence it may seem inactive, but it's just complete). At this point I'm just waiting for people to ask questions or file feature requests under [discussions](https://github.com/bokwoon95/sq/discussions).\n\n## Contributing\n\nSee [START\\_HERE.md](https://github.com/bokwoon95/sq/blob/main/START_HERE.md).\n"
  },
  {
    "path": "START_HERE.md",
    "content": "This document describes how the codebase is organized. It is meant for people who are contributing to the codebase (or are just casually browsing).\n\nFiles are written in such a way that **each successive file in the list below only depends on files that come before it**. This self-enforced restriction makes deep architectural changes trivial because you can essentially blow away the entire codebase and rewrite it from scratch file-by-file, complete with working tests every step of the way. Please adhere to this file order when submitting pull requests.\n\n- [**sq.go**](https://github.com/bokwoon95/sq/blob/main/sq.go)\n    - Core interfaces: SQLWriter, DB, Query, Table, PolicyTable, Window, Field, Predicate, Assignment, Any, Array, Binary, Boolean, Enum, JSON, Number, String, UUID, Time, Enumeration, DialectValuer,\n    - Data types: Result, TableStruct, ViewStruct.\n    - Misc utility functions.\n- [**fmt.go**](https://github.com/bokwoon95/sq/blob/main/fmt.go)\n    - Two important string building functions that everything else is built on: [Writef](https://pkg.go.dev/github.com/bokwoon95/sq#Writef) and [WriteValue](https://pkg.go.dev/github.com/bokwoon95/sq#WriteValue).\n    - Data types: Parameter, BinaryParameter, BooleanParameter, NumberParameter, StringParameter, TimeParameter.\n    - Utility functions: QuoteIdentifier, EscapeQuote, Sprintf, Sprint.\n- [**builtins.go**](https://github.com/bokwoon95/sq/blob/main/builtins.go)\n    - Builtin data types that are built on top of Writef and WriteValue: Expression (Expr), CustomQuery (Queryf), VariadicPredicate, assignment, RowValue, RowValues, Fields.\n    - Builtin functions that are built on top of Writef and WriteValue: Eq, Ne, Lt, Le, Gt, Ge, Exists, NotExists, In.\n- [**fields.go**](https://github.com/bokwoon95/sq/blob/main/fields.go)\n    - All of the field types: AnyField, ArrayField, BinaryField, BooleanField, EnumField, JSONField, NumberField, StringField, UUIDField, TimeField.\n    - Data types: Identifier, Timestamp.\n    - Functions: [New](https://pkg.go.dev/github.com/bokwoon95/sq#New), ArrayValue, EnumValue, JSONValue, UUIDValue.\n- [**cte.go**](https://github.com/bokwoon95/sq/blob/main/cte.go)\n    - CTE represents an SQL common table expression (CTE).\n    - UNION, INTERSECT, EXCEPT.\n- [**joins.go**](https://github.com/bokwoon95/sq/blob/main/joins.go)\n    - The various SQL joins.\n- [**row_column.go**](https://github.com/bokwoon95/sq/blob/main/row_column.go)\n    - Row and Column methods.\n- [**window.go**](https://github.com/bokwoon95/sq/blob/main/window.go)\n    - SQL windows and window functions.\n- [**select_query.go**](https://github.com/bokwoon95/sq/blob/main/select_query.go)\n    - SQL SELECT query builder.\n- [**insert_query.go**](https://github.com/bokwoon95/sq/blob/main/insert_query.go)\n    - SQL INSERT query builder.\n- [**update_query.go**](https://github.com/bokwoon95/sq/blob/main/update_query.go)\n    - SQL UPDATE query builder.\n- [**delete_query.go**](https://github.com/bokwoon95/sq/blob/main/delete_query.go)\n    - SQL DELETE query builder.\n- [**logger.go**](https://github.com/bokwoon95/sq/blob/main/logger.go)\n    - sq.Log and sq.VerboseLog.\n- [**fetch_exec.go**](https://github.com/bokwoon95/sq/blob/main/fetch_exec.go)\n    - FetchCursor, FetchOne, FetchAll, Exec.\n    - CompiledFetch, CompiledExec.\n    - PreparedFetch, PreparedExec.\n- [**misc.go**](https://github.com/bokwoon95/sq/blob/main/misc.go)\n    - Misc SQL constructs.\n    - ValueExpression, LiteralValue, DialectExpression, CaseExpression, SimpleCaseExpression.\n    - SelectValues (`SELECT ... UNION ALL SELECT ... UNION ALL SELECT ...`)\n    - TableValues (`VALUES (...), (...), (...)`).\n- [**integration_test.go**](https://github.com/bokwoon95/sq/blob/main/integration_test.go)\n    - Tests that interact with a live database i.e. SQLite, Postgres, MySQL and SQL Server.\n\n## Testing\n\nAdd tests if you add code.\n\nTo run tests, use:\n\n```shell\n$ go test . # -failfast -shuffle=on -coverprofile=coverage\n```\n\nThere are tests that require a live database connection. They will only run if you provide the corresponding database URL in the test flags:\n\n```shell\n$ go test . -postgres $POSTGRES_URL -mysql $MYSQL_URL -sqlserver $SQLSERVER_URL # -failfast -shuffle=on -coverprofile=coverage\n```\n\nYou can consider using the [docker-compose.yml defined in the sqddl repo](https://github.com/bokwoon95/sqddl/blob/main/docker-compose.yml) to spin up Postgres, MySQL and SQL Server databases that are reachable at the following URLs:\n\n```shell\n# docker-compose up -d\nPOSTGRES_URL='postgres://user1:Hunter2!@localhost:5456/sakila?sslmode=disable'\nMYSQL_URL='root:Hunter2!@tcp(localhost:3330)/sakila?multiStatements=true&parseTime=true'\nMARIADB_URL='root:Hunter2!@tcp(localhost:3340)/sakila?multiStatements=true&parseTime=true'\nSQLSERVER_URL='sqlserver://sa:Hunter2!@localhost:1447'\n```\n\n## Documentation\n\nDocumentation is contained entirely within [sq.md](https://github.com/bokwoon95/sq/blob/main/sq.md) in the project root directory. You can view the output at [https://bokwoon.neocities.org/sq.html](https://bokwoon.neocities.org/sq.html). The documentation is regenerated everytime a new commit is pushed to the main branch, so to change the documentation just change sq.md and submit a pull request.\n\nYou can preview the output of sq.md locally by installing [github.com/bokwoon95/mddocs](https://github.com/bokwoon95/mddocs) and running it with sq.md as the argument.\n\n```shell\n$ go install github/bokwoon95/mddocs@latest\n$ mddocs\nUsage:\nmddocs project.md              # serves project.md on a localhost connection\nmddocs project.md project.html # render project.md into project.html\n\n$ mddocs sq.md\nserving sq.md at localhost:6060\n```\n\nTo add a new section and register it in the table of contents, append a `#headerID` to the end of a header (replace `headerID` with the actual header ID). The header ID should only contain unicode letters, digits, hyphen `-` and underscore `_`.\n\n```text\n## This is a header.\n\n## This is a header with a headerID. #header-id <-- added to table of contents\n```\n"
  },
  {
    "path": "builtins.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n)\n\n// Expression is an SQL expression that satisfies the Table, Field, Predicate,\n// Binary, Boolean, Number, String and Time interfaces.\ntype Expression struct {\n\tformat string\n\tvalues []any\n\talias  string\n}\n\nvar _ interface {\n\tTable\n\tField\n\tPredicate\n\tAny\n\tAssignment\n} = (*Expression)(nil)\n\n// Expr creates a new Expression using Writef syntax.\nfunc Expr(format string, values ...any) Expression {\n\treturn Expression{format: format, values: values}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (expr Expression) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\terr := Writef(ctx, dialect, buf, args, params, expr.format, expr.values)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// As returns a new Expression with the given alias.\nfunc (expr Expression) As(alias string) Expression {\n\texpr.alias = alias\n\treturn expr\n}\n\n// In returns an 'expr IN (value)' Predicate.\nfunc (expr Expression) In(value any) Predicate { return In(expr, value) }\n\n// In returns an 'expr NOT IN (value)' Predicate.\nfunc (expr Expression) NotIn(value any) Predicate { return NotIn(expr, value) }\n\n// Eq returns an 'expr = value' Predicate.\nfunc (expr Expression) Eq(value any) Predicate { return cmp(\"=\", expr, value) }\n\n// Ne returns an 'expr <> value' Predicate.\nfunc (expr Expression) Ne(value any) Predicate { return cmp(\"<>\", expr, value) }\n\n// Lt returns an 'expr < value' Predicate.\nfunc (expr Expression) Lt(value any) Predicate { return cmp(\"<\", expr, value) }\n\n// Le returns an 'expr <= value' Predicate.\nfunc (expr Expression) Le(value any) Predicate { return cmp(\"<=\", expr, value) }\n\n// Gt returns an 'expr > value' Predicate.\nfunc (expr Expression) Gt(value any) Predicate { return cmp(\">\", expr, value) }\n\n// Ge returns an 'expr >= value' Predicate.\nfunc (expr Expression) Ge(value any) Predicate { return cmp(\">=\", expr, value) }\n\n// GetAlias returns the alias of the Expression.\nfunc (expr Expression) GetAlias() string { return expr.alias }\n\n// IsTable implements the Table interface.\nfunc (expr Expression) IsTable() {}\n\n// IsField implements the Field interface.\nfunc (expr Expression) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (expr Expression) IsArray() {}\n\n// IsBinary implements the Binary interface.\nfunc (expr Expression) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (expr Expression) IsBoolean() {}\n\n// IsEnum implements the Enum interface.\nfunc (expr Expression) IsEnum() {}\n\n// IsJSON implements the JSON interface.\nfunc (expr Expression) IsJSON() {}\n\n// IsNumber implements the Number interface.\nfunc (expr Expression) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (expr Expression) IsString() {}\n\n// IsTime implements the Time interface.\nfunc (expr Expression) IsTime() {}\n\n// IsUUID implements the UUID interface.\nfunc (expr Expression) IsUUID() {}\n\nfunc (e Expression) IsAssignment() {}\n\n// CustomQuery represents a user-defined query.\ntype CustomQuery struct {\n\tDialect string\n\tFormat  string\n\tValues  []any\n\tfields  []Field\n}\n\nvar _ Query = (*CustomQuery)(nil)\n\n// Queryf creates a new query using Writef syntax.\nfunc Queryf(format string, values ...any) CustomQuery {\n\treturn CustomQuery{Format: format, Values: values}\n}\n\n// Queryf creates a new SQLite query using Writef syntax.\nfunc (b sqliteQueryBuilder) Queryf(format string, values ...any) CustomQuery {\n\treturn CustomQuery{Dialect: DialectSQLite, Format: format, Values: values}\n}\n\n// Queryf creates a new Postgres query using Writef syntax.\nfunc (b postgresQueryBuilder) Queryf(format string, values ...any) CustomQuery {\n\treturn CustomQuery{Dialect: DialectPostgres, Format: format, Values: values}\n}\n\n// Queryf creates a new MySQL query using Writef syntax.\nfunc (b mysqlQueryBuilder) Queryf(format string, values ...any) CustomQuery {\n\treturn CustomQuery{Dialect: DialectMySQL, Format: format, Values: values}\n}\n\n// Queryf creates a new SQL Server query using Writef syntax.\nfunc (b sqlserverQueryBuilder) Queryf(format string, values ...any) CustomQuery {\n\treturn CustomQuery{Dialect: DialectSQLServer, Format: format, Values: values}\n}\n\n// Append returns a new CustomQuery with the format string and values slice\n// appended to the current CustomQuery.\nfunc (q CustomQuery) Append(format string, values ...any) CustomQuery {\n\tq.Format += \" \" + format\n\tq.Values = append(q.Values, values...)\n\treturn q\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q CustomQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\tformat := q.Format\n\tsplitAt := -1\n\tfor i := strings.IndexByte(format, '{'); i >= 0; i = strings.IndexByte(format, '{') {\n\t\tif i+2 <= len(format) && format[i:i+2] == \"{{\" {\n\t\t\tformat = format[i+2:]\n\t\t\tcontinue\n\t\t}\n\t\tif i+3 <= len(format) && format[i:i+3] == \"{*}\" {\n\t\t\tsplitAt = len(q.Format) - len(format[i:])\n\t\t\tbreak\n\t\t}\n\t\tformat = format[i+1:]\n\t}\n\tif splitAt < 0 {\n\t\treturn Writef(ctx, dialect, buf, args, params, q.Format, q.Values)\n\t}\n\trunningValuesIndex := 0\n\tordinalIndices := make(map[int]int)\n\terr = writef(ctx, dialect, buf, args, params, q.Format[:splitAt], q.Values, &runningValuesIndex, ordinalIndices)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = writeFields(ctx, dialect, buf, args, params, q.fields, true)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = writef(ctx, dialect, buf, args, params, q.Format[splitAt+3:], q.Values, &runningValuesIndex, ordinalIndices)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// SetFetchableFields sets the fetchable fields of the query.\nfunc (q CustomQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\tformat := q.Format\n\tfor i := strings.IndexByte(format, '{'); i >= 0; i = strings.IndexByte(format, '{') {\n\t\tif i+2 <= len(format) && format[i:i+2] == \"{{\" {\n\t\t\tformat = format[i+2:]\n\t\t\tcontinue\n\t\t}\n\t\tif i+3 <= len(format) && format[i:i+3] == \"{*}\" {\n\t\t\tq.fields = fields\n\t\t\treturn q, true\n\t\t}\n\t\tformat = format[i+1:]\n\t}\n\treturn q, false\n}\n\n// GetFetchableFields gets the fetchable fields of the query.\nfunc (q CustomQuery) GetFetchableFields() []Field {\n\treturn q.fields\n}\n\n// GetDialect gets the dialect of the query.\nfunc (q CustomQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q CustomQuery) SetDialect(dialect string) CustomQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// VariadicPredicate represents the 'x AND y AND z...' or 'x OR Y OR z...' SQL\n// construct.\ntype VariadicPredicate struct {\n\t// Toplevel indicates if the VariadicPredicate can skip writing the\n\t// (surrounding brackets).\n\tToplevel bool\n\talias    string\n\t// If IsDisjunction is true, the Predicates are joined using OR. If false,\n\t// the Predicates are joined using AND. The default is AND.\n\tIsDisjunction bool\n\t// Predicates holds the predicates inside the VariadicPredicate\n\tPredicates []Predicate\n}\n\nvar _ Predicate = (*VariadicPredicate)(nil)\n\n// And joins the predicates together with the AND operator.\nfunc And(predicates ...Predicate) VariadicPredicate {\n\treturn VariadicPredicate{IsDisjunction: false, Predicates: predicates}\n}\n\n// Or joins the predicates together with the OR operator.\nfunc Or(predicates ...Predicate) VariadicPredicate {\n\treturn VariadicPredicate{IsDisjunction: true, Predicates: predicates}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (p VariadicPredicate) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\tif len(p.Predicates) == 0 {\n\t\treturn fmt.Errorf(\"VariadicPredicate empty\")\n\t}\n\n\tif len(p.Predicates) == 1 {\n\t\tswitch p1 := p.Predicates[0].(type) {\n\t\tcase nil:\n\t\t\treturn fmt.Errorf(\"predicate #1 is nil\")\n\t\tcase VariadicPredicate:\n\t\t\tp1.Toplevel = p.Toplevel\n\t\t\terr = p1.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\terr = p.Predicates[0].WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tif !p.Toplevel {\n\t\tbuf.WriteString(\"(\")\n\t}\n\tfor i, predicate := range p.Predicates {\n\t\tif i > 0 {\n\t\t\tif p.IsDisjunction {\n\t\t\t\tbuf.WriteString(\" OR \")\n\t\t\t} else {\n\t\t\t\tbuf.WriteString(\" AND \")\n\t\t\t}\n\t\t}\n\t\tswitch predicate := predicate.(type) {\n\t\tcase nil:\n\t\t\treturn fmt.Errorf(\"predicate #%d is nil\", i+1)\n\t\tcase VariadicPredicate:\n\t\t\tpredicate.Toplevel = false\n\t\t\terr = predicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"predicate #%d: %w\", i+1, err)\n\t\t\t}\n\t\tdefault:\n\t\t\terr = predicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"predicate #%d: %w\", i+1, err)\n\t\t\t}\n\t\t}\n\t}\n\tif !p.Toplevel {\n\t\tbuf.WriteString(\")\")\n\t}\n\treturn nil\n}\n\n// As returns a new VariadicPredicate with the given alias.\nfunc (p VariadicPredicate) As(alias string) VariadicPredicate {\n\tp.alias = alias\n\treturn p\n}\n\n// GetAlias returns the alias of the VariadicPredicate.\nfunc (p VariadicPredicate) GetAlias() string { return p.alias }\n\n// IsField implements the Field interface.\nfunc (p VariadicPredicate) IsField() {}\n\n// IsBooleanType implements the Predicate interface.\nfunc (p VariadicPredicate) IsBoolean() {}\n\n// assignment represents assigning a value to a Field.\ntype assignment struct {\n\tfield Field\n\tvalue any\n}\n\nvar _ Assignment = (*assignment)(nil)\n\n// Set creates a new Assignment assigning the value to a field.\nfunc Set(field Field, value any) Assignment {\n\treturn assignment{field: field, value: value}\n}\n\n// Setf creates a new Assignment assigning a custom expression to a Field.\nfunc Setf(field Field, format string, values ...any) Assignment {\n\treturn assignment{field: field, value: Expr(format, values...)}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (a assignment) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tif a.field == nil {\n\t\treturn fmt.Errorf(\"field is nil\")\n\t}\n\tvar err error\n\tif dialect == DialectMySQL {\n\t\terr = a.field.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\terr = withPrefix(a.field, \"\").WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tbuf.WriteString(\" = \")\n\t_, isQuery := a.value.(Query)\n\tif isQuery {\n\t\tbuf.WriteString(\"(\")\n\t}\n\terr = WriteValue(ctx, dialect, buf, args, params, a.value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif isQuery {\n\t\tbuf.WriteString(\")\")\n\t}\n\treturn nil\n}\n\n// IsAssignment implements the Assignment interface.\nfunc (a assignment) IsAssignment() {}\n\n// Assignments represents a list of Assignments e.g. x = 1, y = 2, z = 3.\ntype Assignments []Assignment\n\n// WriteSQL implements the SQLWriter interface.\nfunc (as Assignments) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\tfor i, assignment := range as {\n\t\tif assignment == nil {\n\t\t\treturn fmt.Errorf(\"assignment #%d is nil\", i+1)\n\t\t}\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\terr = assignment.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"assignment #%d: %w\", i+1, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// RowValue represents an SQL row value expression e.g. (x, y, z).\ntype RowValue []any\n\n// WriteSQL implements the SQLWriter interface.\nfunc (r RowValue) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tbuf.WriteString(\"(\")\n\tvar err error\n\tfor i, value := range r {\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\t_, isQuery := value.(Query)\n\t\tif isQuery {\n\t\t\tbuf.WriteString(\"(\")\n\t\t}\n\t\terr = WriteValue(ctx, dialect, buf, args, params, value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"rowvalue #%d: %w\", i+1, err)\n\t\t}\n\t\tif isQuery {\n\t\t\tbuf.WriteString(\")\")\n\t\t}\n\t}\n\tbuf.WriteString(\")\")\n\treturn nil\n}\n\n// In returns an 'rowvalue IN (value)' Predicate.\nfunc (r RowValue) In(v any) Predicate { return In(r, v) }\n\n// NotIn returns an 'rowvalue NOT IN (value)' Predicate.\nfunc (r RowValue) NotIn(v any) Predicate { return NotIn(r, v) }\n\n// Eq returns an 'rowvalue = value' Predicate.\nfunc (r RowValue) Eq(v any) Predicate { return cmp(\"=\", r, v) }\n\n// RowValues represents a list of RowValues e.g. (x, y, z), (a, b, c).\ntype RowValues []RowValue\n\n// WriteSQL implements the SQLWriter interface.\nfunc (rs RowValues) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\tfor i, r := range rs {\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\terr = r.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"rowvalues #%d: %w\", i+1, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// Fields represents a list of Fields e.g. tbl.field1, tbl.field2, tbl.field3.\ntype Fields []Field\n\n// WriteSQL implements the SQLWriter interface.\nfunc (fs Fields) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\tfor i, field := range fs {\n\t\tif field == nil {\n\t\t\treturn fmt.Errorf(\"field #%d is nil\", i+1)\n\t\t}\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\t_, isQuery := field.(Query)\n\t\tif isQuery {\n\t\t\tbuf.WriteString(\"(\")\n\t\t}\n\t\terr = field.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"field #%d: %w\", i+1, err)\n\t\t}\n\t\tif isQuery {\n\t\t\tbuf.WriteString(\")\")\n\t\t}\n\t}\n\treturn nil\n}\n\ntype (\n\tsqliteQueryBuilder    struct{ ctes []CTE }\n\tpostgresQueryBuilder  struct{ ctes []CTE }\n\tmysqlQueryBuilder     struct{ ctes []CTE }\n\tsqlserverQueryBuilder struct{ ctes []CTE }\n)\n\n// Dialect-specific query builder variables.\nvar (\n\tSQLite    sqliteQueryBuilder\n\tPostgres  postgresQueryBuilder\n\tMySQL     mysqlQueryBuilder\n\tSQLServer sqlserverQueryBuilder\n)\n\n// With sets the CTEs in the SQLiteQueryBuilder.\nfunc (b sqliteQueryBuilder) With(ctes ...CTE) sqliteQueryBuilder {\n\tb.ctes = ctes\n\treturn b\n}\n\n// With sets the CTEs in the PostgresQueryBuilder.\nfunc (b postgresQueryBuilder) With(ctes ...CTE) postgresQueryBuilder {\n\tb.ctes = ctes\n\treturn b\n}\n\n// With sets the CTEs in the MySQLQueryBuilder.\nfunc (b mysqlQueryBuilder) With(ctes ...CTE) mysqlQueryBuilder {\n\tb.ctes = ctes\n\treturn b\n}\n\n// With sets the CTEs in the SQLServerQueryBuilder.\nfunc (b sqlserverQueryBuilder) With(ctes ...CTE) sqlserverQueryBuilder {\n\tb.ctes = ctes\n\treturn b\n}\n\n// ToSQL converts an SQLWriter into a query string and args slice.\n//\n// The params map is used to hold the mappings between named parameters in the\n// query to the corresponding index in the args slice and is used for rebinding\n// args by their parameter name. If you don't need to track this, you can pass\n// in a nil map.\nfunc ToSQL(dialect string, w SQLWriter, params map[string][]int) (query string, args []any, err error) {\n\treturn ToSQLContext(context.Background(), dialect, w, params)\n}\n\n// ToSQLContext is like ToSQL but additionally requires a context.Context.\nfunc ToSQLContext(ctx context.Context, dialect string, w SQLWriter, params map[string][]int) (query string, args []any, err error) {\n\tif w == nil {\n\t\treturn \"\", nil, fmt.Errorf(\"SQLWriter is nil\")\n\t}\n\tif dialect == \"\" {\n\t\tif q, ok := w.(Query); ok {\n\t\t\tdialect = q.GetDialect()\n\t\t}\n\t}\n\tbuf := bufpool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer bufpool.Put(buf)\n\terr = w.WriteSQL(ctx, dialect, buf, &args, params)\n\tquery = buf.String()\n\tif err != nil {\n\t\treturn query, args, err\n\t}\n\treturn query, args, nil\n}\n\n// Eq returns an 'x = y' Predicate.\nfunc Eq(x, y any) Predicate { return cmp(\"=\", x, y) }\n\n// Ne returns an 'x <> y' Predicate.\nfunc Ne(x, y any) Predicate { return cmp(\"<>\", x, y) }\n\n// Lt returns an 'x < y' Predicate.\nfunc Lt(x, y any) Predicate { return cmp(\"<\", x, y) }\n\n// Le returns an 'x <= y' Predicate.\nfunc Le(x, y any) Predicate { return cmp(\"<=\", x, y) }\n\n// Gt returns an 'x > y' Predicate.\nfunc Gt(x, y any) Predicate { return cmp(\">\", x, y) }\n\n// Ge returns an 'x >= y' Predicate.\nfunc Ge(x, y any) Predicate { return cmp(\">=\", x, y) }\n\n// Exists returns an 'EXISTS (query)' Predicate.\nfunc Exists(query Query) Predicate { return Expr(\"EXISTS ({})\", query) }\n\n// NotExists returns a 'NOT EXISTS (query)' Predicate.\nfunc NotExists(query Query) Predicate { return Expr(\"NOT EXISTS ({})\", query) }\n\n// In returns an 'x IN (y)' Predicate.\nfunc In(x, y any) Predicate {\n\t_, isQueryA := x.(Query)\n\t_, isRowValueB := y.(RowValue)\n\tif !isQueryA && !isRowValueB {\n\t\treturn Expr(\"{} IN ({})\", x, y)\n\t} else if !isQueryA && isRowValueB {\n\t\treturn Expr(\"{} IN {}\", x, y)\n\t} else if isQueryA && !isRowValueB {\n\t\treturn Expr(\"({}) IN ({})\", x, y)\n\t} else {\n\t\treturn Expr(\"({}) IN {}\", x, y)\n\t}\n}\n\n// NotIn returns an 'x NOT IN (y)' Predicate.\nfunc NotIn(x, y any) Predicate {\n\t_, isQueryA := x.(Query)\n\t_, isRowValueB := y.(RowValue)\n\tif !isQueryA && !isRowValueB {\n\t\treturn Expr(\"{} NOT IN ({})\", x, y)\n\t} else if !isQueryA && isRowValueB {\n\t\treturn Expr(\"{} NOT IN {}\", x, y)\n\t} else if isQueryA && !isRowValueB {\n\t\treturn Expr(\"({}) NOT IN ({})\", x, y)\n\t} else {\n\t\treturn Expr(\"({}) NOT IN {}\", x, y)\n\t}\n}\n\n// cmp returns an 'x <operator> y' Predicate.\nfunc cmp(operator string, x, y any) Expression {\n\t_, isQueryA := x.(Query)\n\t_, isQueryB := y.(Query)\n\tif !isQueryA && !isQueryB {\n\t\treturn Expr(\"{} \"+operator+\" {}\", x, y)\n\t} else if !isQueryA && isQueryB {\n\t\treturn Expr(\"{} \"+operator+\" ({})\", x, y)\n\t} else if isQueryA && !isQueryB {\n\t\treturn Expr(\"({}) \"+operator+\" {}\", x, y)\n\t} else {\n\t\treturn Expr(\"({}) \"+operator+\" ({})\", x, y)\n\t}\n}\n\n// appendPolicy will append a policy from a Table (if it implements\n// PolicyTable) to a slice of policies. The resultant slice is returned.\nfunc appendPolicy(ctx context.Context, dialect string, policies []Predicate, table Table) ([]Predicate, error) {\n\tpolicyTable, ok := table.(PolicyTable)\n\tif !ok {\n\t\treturn policies, nil\n\t}\n\tpolicy, err := policyTable.Policy(ctx, dialect)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif policy != nil {\n\t\tpolicies = append(policies, policy)\n\t}\n\treturn policies, nil\n}\n\n// appendPredicates will append a slices of predicates into a predicate.\nfunc appendPredicates(predicate Predicate, predicates []Predicate) VariadicPredicate {\n\tif predicate == nil {\n\t\treturn And(predicates...)\n\t}\n\tif p1, ok := predicate.(VariadicPredicate); ok && !p1.IsDisjunction {\n\t\tp1.Predicates = append(p1.Predicates, predicates...)\n\t\treturn p1\n\t}\n\tp2 := VariadicPredicate{Predicates: make([]Predicate, 1+len(predicates))}\n\tp2.Predicates[0] = predicate\n\tcopy(p2.Predicates[1:], predicates)\n\treturn p2\n}\n\nfunc writeTop(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, topLimit, topPercentLimit any, withTies bool) error {\n\tvar err error\n\tif topLimit != nil {\n\t\tbuf.WriteString(\"TOP (\")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, topLimit)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"TOP: %w\", err)\n\t\t}\n\t\tbuf.WriteString(\") \")\n\t} else if topPercentLimit != nil {\n\t\tbuf.WriteString(\"TOP (\")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, topPercentLimit)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"TOP PERCENT: %w\", err)\n\t\t}\n\t\tbuf.WriteString(\") PERCENT \")\n\t}\n\tif (topLimit != nil || topPercentLimit != nil) && withTies {\n\t\tbuf.WriteString(\"WITH TIES \")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "builtins_test.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n)\n\nfunc TestExpression(t *testing.T) {\n\tt.Run(\"schema, name and alias\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\texpr := Expr(\"COUNT(*)\").As(\"total\")\n\t\tif diff := testutil.Diff(expr.GetAlias(), \"total\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\ttests := []TestTable{{\n\t\tdescription: \"basic\",\n\t\tdialect:     DialectSQLServer,\n\t\titem:        Expr(\"CONCAT(CONCAT(name, {}), {})\", \"abc\", sql.Named(\"xyz\", \"def\")),\n\t\twantQuery:   \"CONCAT(CONCAT(name, @p1), @xyz)\",\n\t\twantArgs:    []any{\"abc\", sql.Named(\"xyz\", \"def\")},\n\t\twantParams:  map[string][]int{\"xyz\": {1}},\n\t}, {\n\t\tdescription: \"In\", item: Expr(\"age\").In([]int{18, 21, 32}),\n\t\twantQuery: \"age IN (?, ?, ?)\", wantArgs: []any{18, 21, 32},\n\t}, {\n\t\tdescription: \"NotIn\", item: Expr(\"age\").NotIn([]int{18, 21, 32}),\n\t\twantQuery: \"age NOT IN (?, ?, ?)\", wantArgs: []any{18, 21, 32},\n\t}, {\n\t\tdescription: \"Eq\", item: Expr(\"age\").Eq(34),\n\t\twantQuery: \"age = ?\", wantArgs: []any{34},\n\t}, {\n\t\tdescription: \"Ne\", item: Expr(\"age\").Ne(34),\n\t\twantQuery: \"age <> ?\", wantArgs: []any{34},\n\t}, {\n\t\tdescription: \"Lt\", item: Expr(\"age\").Lt(34),\n\t\twantQuery: \"age < ?\", wantArgs: []any{34},\n\t}, {\n\t\tdescription: \"Le\", item: Expr(\"age\").Le(34),\n\t\twantQuery: \"age <= ?\", wantArgs: []any{34},\n\t}, {\n\t\tdescription: \"Gt\", item: Expr(\"age\").Gt(34),\n\t\twantQuery: \"age > ?\", wantArgs: []any{34},\n\t}, {\n\t\tdescription: \"Ge\", item: Expr(\"age\").Ge(34),\n\t\twantQuery: \"age >= ?\", wantArgs: []any{34},\n\t}, {\n\t\tdescription: \"Exists\", item: Exists(Queryf(\"SELECT 1 FROM tbl WHERE 1 = 1\")),\n\t\twantQuery: \"EXISTS (SELECT 1 FROM tbl WHERE 1 = 1)\",\n\t}, {\n\t\tdescription: \"NotExists\", item: NotExists(Queryf(\"SELECT 1 FROM tbl WHERE 1 = 1\")),\n\t\twantQuery: \"NOT EXISTS (SELECT 1 FROM tbl WHERE 1 = 1)\",\n\t}, {\n\t\tdescription: \"Count\", item: Count(Expr(\"name\")), wantQuery: \"COUNT(name)\",\n\t}, {\n\t\tdescription: \"CountStar\", item: CountStar(), wantQuery: \"COUNT(*)\",\n\t}, {\n\t\tdescription: \"Sum\", item: Sum(Expr(\"score\")), wantQuery: \"SUM(score)\",\n\t}, {\n\t\tdescription: \"Avg\", item: Avg(Expr(\"score\")), wantQuery: \"AVG(score)\",\n\t}, {\n\t\tdescription: \"Min\", item: Min(Expr(\"score\")), wantQuery: \"MIN(score)\",\n\t}, {\n\t\tdescription: \"Max\", item: Max(Expr(\"score\")), wantQuery: \"MAX(score)\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n\n\tt.Run(\"FaultySQL\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tTestTable{item: Expr(\"SELECT {}\", FaultySQL{})}.assertNotOK(t)\n\t})\n}\n\nfunc TestVariadicPredicate(t *testing.T) {\n\tt.Run(\"name and alias\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tp := And(Expr(\"True\"), Expr(\"FALSE\")).As(\"is_false\")\n\t\tif diff := testutil.Diff(p.GetAlias(), \"is_false\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"empty\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = VariadicPredicate{}\n\t\ttt.assertNotOK(t)\n\t})\n\n\tt.Run(\"nil predicate\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = And(nil)\n\t\ttt.assertNotOK(t)\n\t})\n\n\tt.Run(\"1 predicate\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = And(cmp(\"=\", Expr(\"score\"), 21))\n\t\ttt.wantQuery = \"score = ?\"\n\t\ttt.wantArgs = []any{21}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"2 predicate\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = And(cmp(\"=\", Expr(\"score\"), 21), cmp(\"=\", Expr(\"name\"), \"bob\"))\n\t\ttt.wantQuery = \"(score = ? AND name = ?)\"\n\t\ttt.wantArgs = []any{21, \"bob\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"multiple nested VariadicPredicate collapses into one\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = And(And(And(And(cmp(\"=\", Expr(\"score\"), 21)))))\n\t\ttt.wantQuery = \"score = ?\"\n\t\ttt.wantArgs = []any{21}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"multiple predicates\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\tuser_id, name, age := Expr(\"user_id\"), Expr(\"name\"), Expr(\"age\")\n\t\ttt.item = Or(\n\t\t\tExpr(\"{} IS NULL\", name),\n\t\t\tcmp(\"=\", age, age),\n\t\t\tAnd(cmp(\"=\", age, age)),\n\t\t\tAnd(\n\t\t\t\tcmp(\"=\", user_id, 1),\n\t\t\t\tcmp(\"<>\", user_id, 2),\n\t\t\t\tcmp(\"<\", user_id, 3),\n\t\t\t\tcmp(\"<=\", user_id, 4),\n\t\t\t\tcmp(\">\", user_id, 5),\n\t\t\t\tcmp(\">=\", user_id, 6),\n\t\t\t),\n\t\t)\n\t\ttt.wantQuery = \"(name IS NULL\" +\n\t\t\t\" OR age = age\" +\n\t\t\t\" OR age = age\" +\n\t\t\t\" OR (\" +\n\t\t\t\"user_id = ?\" +\n\t\t\t\" AND user_id <> ?\" +\n\t\t\t\" AND user_id < ?\" +\n\t\t\t\" AND user_id <= ?\" +\n\t\t\t\" AND user_id > ?\" +\n\t\t\t\" AND user_id >= ?\" +\n\t\t\t\"))\"\n\t\ttt.wantArgs = []any{1, 2, 3, 4, 5, 6}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"multiple predicates with nil\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Or(\n\t\t\tExpr(\"1 = 1\"),\n\t\t\tAnd(Expr(\"TRUE\"), Predicate(nil)),\n\t\t)\n\t\ttt.assertNotOK(t)\n\t})\n\n\tt.Run(\"VariadicPredicate alias\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tp1 := And(Expr(\"TRUE\")).As(\"abc\")\n\t\tif diff := testutil.Diff(p1.GetAlias(), \"abc\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tp2 := p1.As(\"def\")\n\t\tif diff := testutil.Diff(p1.GetAlias(), \"abc\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif diff := testutil.Diff(p2.GetAlias(), \"def\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"VariadicPredicate FaultySQL\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\t// AND, 1 predicate\n\t\ttt.item = And(FaultySQL{})\n\t\ttt.assertErr(t, ErrFaultySQL)\n\t\t// AND, multiple predicates\n\t\ttt.item = And(Expr(\"FALSE\"), FaultySQL{})\n\t\ttt.assertErr(t, ErrFaultySQL)\n\t\t// nested AND\n\t\ttt.item = And(And(FaultySQL{}))\n\t\ttt.assertErr(t, ErrFaultySQL)\n\t})\n}\n\nfunc TestQueryf(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Queryf(\"SELECT {field} FROM {table} WHERE {predicate}\",\n\t\t\tsql.Named(\"field\", Expr(\"name\")),\n\t\t\tsql.Named(\"table\", Expr(\"users\")),\n\t\t\tsql.Named(\"predicate\", Expr(\"user_id = {}\", 5)),\n\t\t)\n\t\ttt.wantQuery = \"SELECT name FROM users WHERE user_id = ?\"\n\t\ttt.wantArgs = []any{5}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"select star\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\tq := Queryf(\"SELECT {*} FROM {table} WHERE {predicate}\",\n\t\t\tsql.Named(\"table\", Expr(\"users\")),\n\t\t\tsql.Named(\"predicate\", Expr(\"user_id = {}\", 5)),\n\t\t)\n\t\tif diff := testutil.Diff(q.GetDialect(), \"\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq2, ok := q.SetFetchableFields([]Field{Expr(\"name\"), Expr(\"age\")})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"not ok\")\n\t\t}\n\t\ttt.item = q2\n\t\ttt.wantQuery = \"SELECT name, age FROM users WHERE user_id = ?\"\n\t\ttt.wantArgs = []any{5}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"escape curly brace\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\tq := Queryf(`WITH cte AS (SELECT '{{*}' AS name) SELECT {*} FROM cte`)\n\t\tq2, ok := q.SetFetchableFields([]Field{Expr(\"name\")})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"not ok\")\n\t\t}\n\t\ttt.item = q2\n\t\ttt.wantQuery = \"WITH cte AS (SELECT '{*}' AS name) SELECT name FROM cte\"\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"mixed\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\tq := Queryf(`{1} {3} {name} {} SELECT {*} FROM {1} {} {name} {}`, 5, sql.Named(\"name\", \"bob\"), 10)\n\t\tq2, ok := q.SetFetchableFields([]Field{Expr(\"alpha\"), Expr(\"SUBSTR({}, {})\", \"apple\", 77), Expr(\"beta\")})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"not ok\")\n\t\t}\n\t\ttt.dialect = DialectPostgres\n\t\ttt.item = q2\n\t\ttt.wantQuery = \"$1 $2 $3 $4 SELECT alpha, SUBSTR($5, $6), beta FROM $1 $3 $3 $7\"\n\t\ttt.wantArgs = []any{5, 10, \"bob\", 5, \"apple\", 77, 10}\n\t\ttt.wantParams = map[string][]int{\"name\": {2}}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"append\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\tq := Queryf(\"SELECT {*} FROM tbl WHERE 1 = 1\")\n\t\tq = q.Append(\"AND name = {}\", \"bob\")\n\t\tq = q.Append(\"AND email = {email}\", sql.Named(\"email\", \"bob@email.com\"))\n\t\tq = q.Append(\"AND age = {age}\", sql.Named(\"age\", 27))\n\t\tq2, ok := q.SetFetchableFields([]Field{Expr(\"name\"), Expr(\"email\")})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"not ok\")\n\t\t}\n\t\ttt.item = q2\n\t\ttt.wantQuery = \"SELECT name, email FROM tbl WHERE 1 = 1 AND name = ? AND email = ? AND age = ?\"\n\t\ttt.wantArgs = []any{\"bob\", \"bob@email.com\", 27}\n\t\ttt.wantParams = map[string][]int{\"email\": {1}, \"age\": {2}}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestAssign(t *testing.T) {\n\tt.Run(\"AssignValue nil field\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t_, _, err := ToSQL(\"\", Set(nil, 1), nil)\n\t\tif err == nil {\n\t\t\tt.Error(testutil.Callers(), \"expected error but got nil\")\n\t\t}\n\t})\n\n\tt.Run(\"AssignValue\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tTestTable{\n\t\t\titem:      Set(tmpfield(\"tbl.field\"), 1),\n\t\t\twantQuery: \"field = ?\", wantArgs: []any{1},\n\t\t}.assert(t)\n\t})\n\n\tt.Run(\"mysql AssignValue\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tTestTable{\n\t\t\tdialect:   DialectMySQL,\n\t\t\titem:      Set(tmpfield(\"tbl.field\"), 1),\n\t\t\twantQuery: \"tbl.field = ?\", wantArgs: []any{1},\n\t\t}.assert(t)\n\t})\n\n\tt.Run(\"AssignValue err\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tTestTable{\n\t\t\titem: Set(tmpfield(\"tbl.field\"), FaultySQL{}),\n\t\t}.assertErr(t, ErrFaultySQL)\n\t})\n\n\tt.Run(\"Assignf nil field\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t_, _, err := ToSQL(\"\", Setf(nil, \"\"), nil)\n\t\tif err == nil {\n\t\t\tt.Error(testutil.Callers(), \"expected error but got nil\")\n\t\t}\n\t})\n\n\tt.Run(\"Assignf\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tTestTable{\n\t\t\titem:      Setf(tmpfield(\"tbl.field\"), \"EXCLUDED.field\"),\n\t\t\twantQuery: \"field = EXCLUDED.field\",\n\t\t}.assert(t)\n\t})\n\n\tt.Run(\"Assignf err\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tTestTable{\n\t\t\titem: Setf(tmpfield(\"tbl.field\"), \"EXCLUDED.{}\", FaultySQL{}),\n\t\t}.assertErr(t, ErrFaultySQL)\n\t})\n}\n\nfunc TestRowValuesFieldsAssignments(t *testing.T) {\n\ttests := []TestTable{{\n\t\tdescription: \"RowValue\", item: RowValue{1, 2, 3},\n\t\twantQuery: \"(?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"RowValue with query\",\n\t\titem:        RowValue{1, 2, Queryf(\"SELECT {}\", 3)},\n\t\twantQuery:   \"(?, ?, (SELECT ?))\",\n\t\twantArgs:    []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"RowValue In\",\n\t\titem:        RowValue{1, 2, 3}.In(RowValues{{4, 5, 6}, {7, 8, 9}}),\n\t\twantQuery:   \"(?, ?, ?) IN ((?, ?, ?), (?, ?, ?))\",\n\t\twantArgs:    []any{1, 2, 3, 4, 5, 6, 7, 8, 9},\n\t}, {\n\t\tdescription: \"RowValue NotIn\",\n\t\titem:        RowValue{1, 2, 3}.NotIn(RowValues{{4, 5, 6}, {7, 8, 9}}),\n\t\twantQuery:   \"(?, ?, ?) NOT IN ((?, ?, ?), (?, ?, ?))\",\n\t\twantArgs:    []any{1, 2, 3, 4, 5, 6, 7, 8, 9},\n\t}, {\n\t\tdescription: \"RowValue Eq\",\n\t\titem:        RowValue{1, 2, 3}.Eq(RowValue{4, 5, 6}),\n\t\twantQuery:   \"(?, ?, ?) = (?, ?, ?)\",\n\t\twantArgs:    []any{1, 2, 3, 4, 5, 6},\n\t}, {\n\t\tdescription: \"Fields\",\n\t\titem:        Fields{Expr(\"tbl.f1\"), Expr(\"tbl.f2\"), Expr(\"tbl.f3\")},\n\t\twantQuery:   \"tbl.f1, tbl.f2, tbl.f3\",\n\t}, {\n\t\tdescription: \"Assignments\",\n\t\titem:        Assignments{Set(Expr(\"f1\"), 1), Set(Expr(\"f2\"), 2), Set(Expr(\"f3\"), 3)},\n\t\twantQuery:   \"f1 = ?, f2 = ?, f3 = ?\", wantArgs: []any{1, 2, 3},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n\n\tt.Run(\"nil fields/assignments\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t// Fields\n\t\tTestTable{item: Fields{Expr(\"f1\"), Expr(\"f2\"), nil}}.assertNotOK(t)\n\t\t// Assignments\n\t\tTestTable{item: Assignments{Set(Expr(\"f1\"), 1), Set(Expr(\"f2\"), 2), nil}}.assertNotOK(t)\n\t})\n\n\terrTests := []TestTable{{\n\t\tdescription: \"RowValue err\", item: RowValue{1, 2, FaultySQL{}},\n\t}, {\n\t\tdescription: \"RowValues err\", item: RowValues{{1, 2, FaultySQL{}}},\n\t}, {\n\t\tdescription: \"Fields err\", item: Fields{Expr(\"f1\"), Expr(\"f2\"), FaultySQL{}},\n\t}, {\n\t\tdescription: \"Assignments err\",\n\t\titem:        Assignments{Set(Expr(\"f1\"), 1), Set(Expr(\"f2\"), 2), FaultySQL{}},\n\t}}\n\n\tfor _, tt := range errTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertErr(t, ErrFaultySQL)\n\t\t})\n\t}\n}\n\nfunc TestToSQL(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tgotQuery, _, err := ToSQL(\"\", Queryf(\"SELECT {fields} FROM {table}\",\n\t\t\tsql.Named(\"fields\", Fields{Expr(\"f1\"), Expr(\"f2\"), Expr(\"f3\")}),\n\t\t\tsql.Named(\"table\", Expr(\"tbl\")),\n\t\t), nil)\n\t\tif err != nil {\n\t\t\tt.Error(testutil.Callers(), err)\n\t\t}\n\t\twantQuery := \"SELECT f1, f2, f3 FROM tbl\"\n\t\tif diff := testutil.Diff(gotQuery, wantQuery); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"nil SQLWriter\", func(t *testing.T) {\n\t\t_, _, err := ToSQL(\"\", nil, nil)\n\t\tif err == nil {\n\t\t\tt.Error(testutil.Callers(), \"expected error but got nil\")\n\t\t}\n\t})\n\n\tt.Run(\"err\", func(t *testing.T) {\n\t\t_, _, err := ToSQL(\"\", Queryf(\"SELECT {}\", FaultySQL{}), nil)\n\t\tif !errors.Is(err, ErrFaultySQL) {\n\t\t\tt.Errorf(testutil.Callers()+\"expected '%v' but got '%v'\", ErrFaultySQL, err)\n\t\t}\n\t})\n}\n\nfunc Test_in_cmp(t *testing.T) {\n\ttests := []TestTable{{\n\t\tdescription: \"!Query IN !RowValue\",\n\t\titem:        In(Expr(\"{}\", \"tom\"), Queryf(\"SELECT name FROM users WHERE name LIKE {}\", \"t%\")),\n\t\twantQuery:   \"? IN (SELECT name FROM users WHERE name LIKE ?)\", wantArgs: []any{\"tom\", \"t%\"},\n\t}, {\n\t\tdescription: \"!Query IN RowValue\", item: In(Expr(\"name\"), RowValue{\"tom\", \"dick\", \"harry\"}),\n\t\twantQuery: \"name IN (?, ?, ?)\", wantArgs: []any{\"tom\", \"dick\", \"harry\"},\n\t}, {\n\t\tdescription: \"Query IN !RowValue\", item: In(Queryf(\"SELECT {}\", \"tom\"), []string{\"tom\", \"dick\", \"harry\"}),\n\t\twantQuery: \"(SELECT ?) IN (?, ?, ?)\", wantArgs: []any{\"tom\", \"tom\", \"dick\", \"harry\"},\n\t}, {\n\t\tdescription: \"Query IN RowValue\", item: In(Queryf(\"SELECT {}\", \"tom\"), RowValue{\"tom\", \"dick\", \"harry\"}),\n\t\twantQuery: \"(SELECT ?) IN (?, ?, ?)\", wantArgs: []any{\"tom\", \"tom\", \"dick\", \"harry\"},\n\t}, {\n\t\tdescription: \"!Query NOT IN !RowValue\",\n\t\titem:        NotIn(Expr(\"{}\", \"tom\"), Queryf(\"SELECT name FROM users WHERE name LIKE {}\", \"t%\")),\n\t\twantQuery:   \"? NOT IN (SELECT name FROM users WHERE name LIKE ?)\", wantArgs: []any{\"tom\", \"t%\"},\n\t}, {\n\t\tdescription: \"!Query NOT IN RowValue\", item: NotIn(Expr(\"name\"), RowValue{\"tom\", \"dick\", \"harry\"}),\n\t\twantQuery: \"name NOT IN (?, ?, ?)\", wantArgs: []any{\"tom\", \"dick\", \"harry\"},\n\t}, {\n\t\tdescription: \"Query NOT IN !RowValue\", item: NotIn(Queryf(\"SELECT {}\", \"tom\"), []string{\"tom\", \"dick\", \"harry\"}),\n\t\twantQuery: \"(SELECT ?) NOT IN (?, ?, ?)\", wantArgs: []any{\"tom\", \"tom\", \"dick\", \"harry\"},\n\t}, {\n\t\tdescription: \"Query NOT IN RowValue\", item: NotIn(Queryf(\"SELECT {}\", \"tom\"), RowValue{\"tom\", \"dick\", \"harry\"}),\n\t\twantQuery: \"(SELECT ?) NOT IN (?, ?, ?)\", wantArgs: []any{\"tom\", \"tom\", \"dick\", \"harry\"},\n\t}, {\n\t\tdescription: \"!Query = !Query\", item: cmp(\"=\", 1, 1),\n\t\twantQuery: \"? = ?\", wantArgs: []any{1, 1},\n\t}, {\n\t\tdescription: \"!Query = Query\", item: cmp(\"=\", Expr(\"score\"), Queryf(\"SELECT score FROM users WHERE id = {}\", 5)),\n\t\twantQuery: \"score = (SELECT score FROM users WHERE id = ?)\", wantArgs: []any{5},\n\t}, {\n\t\tdescription: \"Query = !Query\", item: cmp(\"=\", Queryf(\"SELECT score FROM users WHERE id = {}\", 5), Expr(\"{}\", 7)),\n\t\twantQuery: \"(SELECT score FROM users WHERE id = ?) = ?\", wantArgs: []any{5, 7},\n\t}, {\n\t\tdescription: \"Query = Query\", item: cmp(\"=\", Queryf(\"SELECT 1\"), Queryf(\"SELECT 2\")),\n\t\twantQuery: \"(SELECT 1) = (SELECT 2)\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\ntype policyTableStub struct {\n\tpolicy Predicate\n\terr    error\n}\n\nvar _ PolicyTable = (*policyTableStub)(nil)\n\nfunc (tbl policyTableStub) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tbuf.WriteString(\"policy_table_stub\")\n\treturn nil\n}\n\nfunc (tbl policyTableStub) GetAlias() string { return \"\" }\n\nfunc (tbl policyTableStub) Policy(ctx context.Context, dialect string) (Predicate, error) {\n\treturn tbl.policy, tbl.err\n}\n\nfunc (tbl policyTableStub) IsTable() {}\n\nfunc Test_appendPolicy(t *testing.T) {\n\ttype TT struct {\n\t\tdescription  string\n\t\ttable        Table\n\t\twantPolicies []Predicate\n\t\twantErr      error\n\t}\n\n\ttests := []TT{{\n\t\tdescription: \"table doesn't implement PolicyTable\",\n\t\ttable:       Expr(\"tbl\"),\n\t}, {\n\t\tdescription: \"PolicyTable returns err\",\n\t\ttable:       policyTableStub{err: ErrFaultySQL},\n\t\twantErr:     ErrFaultySQL,\n\t}, {\n\t\tdescription:  \"PolicyTable returns policy\",\n\t\ttable:        policyTableStub{policy: Expr(\"TRUE\")},\n\t\twantPolicies: []Predicate{Expr(\"TRUE\")},\n\t}, {\n\t\tdescription: \"PolicyTable returns nil policy\",\n\t\ttable:       policyTableStub{},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tpolicies, err := appendPolicy(context.Background(), \"\", nil, tt.table)\n\t\t\tif !errors.Is(err, tt.wantErr) {\n\t\t\t\tt.Errorf(testutil.Callers()+\"expected error '%v' but got '%v'\", tt.wantErr, err)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(policies, tt.wantPolicies); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_appendPredicates(t *testing.T) {\n\ttype TT struct {\n\t\tdescription   string\n\t\tpredicate     Predicate\n\t\tpredicates    []Predicate\n\t\twantPredicate VariadicPredicate\n\t}\n\n\tp1, p2, p3 := Expr(\"p1\"), Expr(\"p2\"), Expr(\"p3\")\n\ttests := []TT{{\n\t\tdescription: \"nil predicate\",\n\t\tpredicate:   nil, predicates: []Predicate{p2, p3},\n\t\twantPredicate: And(p2, p3),\n\t}, {\n\t\tdescription: \"AND predicate\",\n\t\tpredicate:   And(p1), predicates: []Predicate{p2, p3},\n\t\twantPredicate: And(p1, p2, p3),\n\t}, {\n\t\tdescription: \"OR predicate\",\n\t\tpredicate:   Or(p1), predicates: []Predicate{p2, p3},\n\t\twantPredicate: And(Or(p1), p2, p3),\n\t}, {\n\t\tdescription: \"non-VariadicPredicate predicate\",\n\t\tpredicate:   p1, predicates: []Predicate{p2, p3},\n\t\twantPredicate: And(p1, p2, p3),\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tgotPredicate := appendPredicates(tt.predicate, tt.predicates)\n\t\t\tif diff := testutil.Diff(gotPredicate, tt.wantPredicate); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_writeTop(t *testing.T) {\n\ttype TT struct {\n\t\tdescription     string\n\t\ttopLimit        any\n\t\ttopPercentLimit any\n\t\twithTies        bool\n\t\twantQuery       string\n\t\twantArgs        []any\n\t\twantParams      map[string][]int\n\t}\n\n\tt.Run(\"err\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tctx := context.Background()\n\t\tdialect := DialectSQLServer\n\t\tbuf := &bytes.Buffer{}\n\t\targs := &[]any{}\n\t\tparams := make(map[string][]int)\n\t\t// topLimit\n\t\terr := writeTop(ctx, dialect, buf, args, params, FaultySQL{}, nil, false)\n\t\tif !errors.Is(err, ErrFaultySQL) {\n\t\t\tt.Errorf(testutil.Callers()+\"expected error '%v' but got '%v'\", ErrFaultySQL, err)\n\t\t}\n\t\t// topPercentLimit\n\t\terr = writeTop(ctx, dialect, buf, args, params, nil, FaultySQL{}, false)\n\t\tif !errors.Is(err, ErrFaultySQL) {\n\t\t\tt.Errorf(testutil.Callers()+\"expected error '%v' but got '%v'\", ErrFaultySQL, err)\n\t\t}\n\t})\n\n\ttests := []TT{{\n\t\tdescription: \"empty\", topLimit: nil, topPercentLimit: nil, withTies: true,\n\t\twantQuery: \"\", wantArgs: nil, wantParams: nil,\n\t}, {\n\t\tdescription: \"TOP n\", topLimit: 5,\n\t\twantQuery: \"TOP (@p1) \", wantArgs: []any{5},\n\t}, {\n\t\tdescription: \"TOP n PERCENT\", topPercentLimit: 10,\n\t\twantQuery: \"TOP (@p1) PERCENT \", wantArgs: []any{10},\n\t}, {\n\t\tdescription: \"TOP n WITH TIES\", topLimit: 5, withTies: true,\n\t\twantQuery: \"TOP (@p1) WITH TIES \", wantArgs: []any{5},\n\t}, {\n\t\tdescription: \"TOP expr\", topLimit: Expr(\"5\"),\n\t\twantQuery: \"TOP (5) \",\n\t}, {\n\t\tdescription: \"TOP param\", topLimit: IntParam(\"limit\", 5),\n\t\twantQuery: \"TOP (@limit) \", wantArgs: []any{sql.Named(\"limit\", 5)},\n\t\twantParams: map[string][]int{\"limit\": {0}},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tctx := context.Background()\n\t\t\tdialect := DialectSQLServer\n\t\t\tbuf := &bytes.Buffer{}\n\t\t\targs := &[]any{}\n\t\t\tparams := make(map[string][]int)\n\t\t\terr := writeTop(ctx, dialect, buf, args, params, tt.topLimit, tt.topPercentLimit, tt.withTies)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(testutil.Callers(), err)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(buf.String(), tt.wantQuery); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(*args, tt.wantArgs); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t\tif len(params) > 0 || len(tt.wantParams) > 0 {\n\t\t\t\tif diff := testutil.Diff(params, tt.wantParams); diff != \"\" {\n\t\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TODO: remove all uses of TestTable, manually write each assert. Fewer levels\n// of BS indirection when you need to debug something.\ntype TestTable struct {\n\tdescription string\n\tctx         context.Context\n\tdialect     string\n\titem        any\n\twantQuery   string\n\twantArgs    []any\n\twantParams  map[string][]int\n}\n\nfunc (tt TestTable) assert(t *testing.T) {\n\tif tt.ctx == nil {\n\t\ttt.ctx = context.Background()\n\t}\n\tif tt.dialect == \"\" {\n\t\tif query, ok := tt.item.(Query); ok {\n\t\t\ttt.dialect = query.GetDialect()\n\t\t}\n\t}\n\tbuf := &bytes.Buffer{}\n\targs := &[]any{}\n\tparams := make(map[string][]int)\n\terr := WriteValue(tt.ctx, tt.dialect, buf, args, params, tt.item)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(buf.String(), tt.wantQuery); diff != \"\" {\n\t\tt.Error(testutil.Callers(), diff)\n\t}\n\tif len(*args) > 0 || len(tt.wantArgs) > 0 {\n\t\tif diff := testutil.Diff(*args, tt.wantArgs); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t}\n\tif len(params) > 0 || len(tt.wantParams) > 0 {\n\t\tif diff := testutil.Diff(params, tt.wantParams); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t}\n}\n\nfunc (tt TestTable) assertErr(t *testing.T, wantErr error) {\n\tif tt.ctx == nil {\n\t\ttt.ctx = context.Background()\n\t}\n\tif tt.dialect == \"\" {\n\t\tif query, ok := tt.item.(Query); ok {\n\t\t\ttt.dialect = query.GetDialect()\n\t\t}\n\t}\n\tbuf := &bytes.Buffer{}\n\targs := &[]any{}\n\tparams := make(map[string][]int)\n\tgotErr := WriteValue(tt.ctx, tt.dialect, buf, args, params, tt.item)\n\tif !errors.Is(gotErr, wantErr) {\n\t\tt.Fatalf(testutil.Callers()+\"expected error '%v' but got '%v'\", wantErr, gotErr)\n\t}\n}\n\nfunc (tt TestTable) assertNotOK(t *testing.T) {\n\tif tt.ctx == nil {\n\t\ttt.ctx = context.Background()\n\t}\n\tif tt.dialect == \"\" {\n\t\tif query, ok := tt.item.(Query); ok {\n\t\t\ttt.dialect = query.GetDialect()\n\t\t}\n\t}\n\tbuf := &bytes.Buffer{}\n\targs := &[]any{}\n\tparams := make(map[string][]int)\n\tgotErr := WriteValue(tt.ctx, tt.dialect, buf, args, params, tt.item)\n\tif gotErr == nil {\n\t\tt.Fatal(testutil.Callers(), \"expected error but got nil\")\n\t}\n}\n"
  },
  {
    "path": "colors.go",
    "content": "//go:build windows\n\npackage sq\n\nimport (\n\t\"os\"\n\t\"syscall\"\n)\n\nfunc init() {\n\t// https://stackoverflow.com/a/69542231\n\tconst ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4\n\tvar stderrMode uint32\n\tstderr := syscall.Handle(os.Stderr.Fd())\n\tsyscall.GetConsoleMode(stderr, &stderrMode)\n\tsyscall.MustLoadDLL(\"kernel32\").MustFindProc(\"SetConsoleMode\").Call(uintptr(stderr), uintptr(stderrMode|ENABLE_VIRTUAL_TERMINAL_PROCESSING))\n\tvar stdoutMode uint32\n\tstdout := syscall.Handle(os.Stdout.Fd())\n\tsyscall.GetConsoleMode(stdout, &stdoutMode)\n\tsyscall.MustLoadDLL(\"kernel32\").MustFindProc(\"SetConsoleMode\").Call(uintptr(stdout), uintptr(stdoutMode|ENABLE_VIRTUAL_TERMINAL_PROCESSING))\n}\n"
  },
  {
    "path": "cte.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n)\n\n// CTE represents an SQL common table expression (CTE).\ntype CTE struct {\n\tquery        Query\n\tcolumns      []string\n\trecursive    bool\n\tmaterialized sql.NullBool\n\tname         string\n\talias        string\n}\n\nvar _ Table = (*CTE)(nil)\n\n// NewCTE creates a new CTE.\nfunc NewCTE(name string, columns []string, query Query) CTE {\n\treturn CTE{name: name, columns: columns, query: query}\n}\n\n// NewRecursiveCTE creates a new recursive CTE.\nfunc NewRecursiveCTE(name string, columns []string, query Query) CTE {\n\treturn CTE{name: name, columns: columns, query: query, recursive: true}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (cte CTE) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tbuf.WriteString(QuoteIdentifier(dialect, cte.name))\n\treturn nil\n}\n\n// As returns a new CTE with the given alias.\nfunc (cte CTE) As(alias string) CTE {\n\tcte.alias = alias\n\treturn cte\n}\n\n// Materialized returns a new CTE marked as MATERIALIZED. This only works on\n// postgres.\nfunc (cte CTE) Materialized() CTE {\n\tcte.materialized.Valid = true\n\tcte.materialized.Bool = true\n\treturn cte\n}\n\n// Materialized returns a new CTE marked as NOT MATERIALIZED. This only works\n// on postgres.\nfunc (cte CTE) NotMaterialized() CTE {\n\tcte.materialized.Valid = true\n\tcte.materialized.Bool = false\n\treturn cte\n}\n\n// Field returns a Field from the CTE.\nfunc (cte CTE) Field(name string) AnyField {\n\treturn NewAnyField(name, NewTableStruct(\"\", cte.name, cte.alias))\n}\n\n// GetAlias returns the alias of the CTE.\nfunc (cte CTE) GetAlias() string { return cte.alias }\n\n// AssertTable implements the Table interface.\nfunc (cte CTE) IsTable() {}\n\nfunc writeCTEs(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, ctes []CTE) error {\n\tvar hasRecursiveCTE bool\n\tfor _, cte := range ctes {\n\t\tif cte.recursive {\n\t\t\thasRecursiveCTE = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif hasRecursiveCTE {\n\t\tbuf.WriteString(\"WITH RECURSIVE \")\n\t} else {\n\t\tbuf.WriteString(\"WITH \")\n\t}\n\tfor i, cte := range ctes {\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\tif cte.name == \"\" {\n\t\t\treturn fmt.Errorf(\"CTE #%d has no name\", i+1)\n\t\t}\n\t\tbuf.WriteString(QuoteIdentifier(dialect, cte.name))\n\t\tif len(cte.columns) > 0 {\n\t\t\tbuf.WriteString(\" (\")\n\t\t\tfor j, column := range cte.columns {\n\t\t\t\tif j > 0 {\n\t\t\t\t\tbuf.WriteString(\", \")\n\t\t\t\t}\n\t\t\t\tbuf.WriteString(QuoteIdentifier(dialect, column))\n\t\t\t}\n\t\t\tbuf.WriteString(\")\")\n\t\t}\n\t\tbuf.WriteString(\" AS \")\n\t\tif dialect == DialectPostgres && cte.materialized.Valid {\n\t\t\tif cte.materialized.Bool {\n\t\t\t\tbuf.WriteString(\"MATERIALIZED \")\n\t\t\t} else {\n\t\t\t\tbuf.WriteString(\"NOT MATERIALIZED \")\n\t\t\t}\n\t\t}\n\t\tbuf.WriteString(\"(\")\n\t\tswitch query := cte.query.(type) {\n\t\tcase nil:\n\t\t\treturn fmt.Errorf(\"CTE #%d query is nil\", i+1)\n\t\tcase VariadicQuery:\n\t\t\tquery.Toplevel = true\n\t\t\terr := query.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"CTE #%d failed to build query: %w\", i+1, err)\n\t\t\t}\n\t\tdefault:\n\t\t\terr := query.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"CTE #%d failed to build query: %w\", i+1, err)\n\t\t\t}\n\t\t}\n\t\tbuf.WriteString(\")\")\n\t}\n\tbuf.WriteString(\" \")\n\treturn nil\n}\n\n// VariadicQueryOperator represents a variadic query operator.\ntype VariadicQueryOperator string\n\n// VariadicQuery operators.\nconst (\n\tQueryUnion        VariadicQueryOperator = \"UNION\"\n\tQueryUnionAll     VariadicQueryOperator = \"UNION ALL\"\n\tQueryIntersect    VariadicQueryOperator = \"INTERSECT\"\n\tQueryIntersectAll VariadicQueryOperator = \"INTERSECT ALL\"\n\tQueryExcept       VariadicQueryOperator = \"EXCEPT\"\n\tQueryExceptAll    VariadicQueryOperator = \"EXCEPT ALL\"\n)\n\n// VariadicQuery represents the 'x UNION y UNION z...' etc SQL constructs.\ntype VariadicQuery struct {\n\tToplevel bool\n\tOperator VariadicQueryOperator\n\tQueries  []Query\n}\n\nvar _ Query = (*VariadicQuery)(nil)\n\n// Union joins the queries together with the UNION operator.\nfunc Union(queries ...Query) VariadicQuery {\n\treturn VariadicQuery{Operator: QueryUnion, Queries: queries}\n}\n\n// UnionAll joins the queries together with the UNION ALL operator.\nfunc UnionAll(queries ...Query) VariadicQuery {\n\treturn VariadicQuery{Operator: QueryUnionAll, Queries: queries}\n}\n\n// Intersect joins the queries together with the INTERSECT operator.\nfunc Intersect(queries ...Query) VariadicQuery {\n\treturn VariadicQuery{Operator: QueryIntersect, Queries: queries}\n}\n\n// IntersectAll joins the queries together with the INTERSECT ALL operator.\nfunc IntersectAll(queries ...Query) VariadicQuery {\n\treturn VariadicQuery{Operator: QueryIntersectAll, Queries: queries}\n}\n\n// Except joins the queries together with the EXCEPT operator.\nfunc Except(queries ...Query) VariadicQuery {\n\treturn VariadicQuery{Operator: QueryExcept, Queries: queries}\n}\n\n// ExceptAll joins the queries together with the EXCEPT ALL operator.\nfunc ExceptAll(queries ...Query) VariadicQuery {\n\treturn VariadicQuery{Operator: QueryExceptAll, Queries: queries}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q VariadicQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\tif q.Operator == \"\" {\n\t\tq.Operator = QueryUnion\n\t}\n\tif len(q.Queries) == 0 {\n\t\treturn fmt.Errorf(\"VariadicQuery empty\")\n\t}\n\n\tif len(q.Queries) == 1 {\n\t\tswitch q1 := q.Queries[0].(type) {\n\t\tcase nil:\n\t\t\treturn fmt.Errorf(\"query #1 is nil\")\n\t\tcase VariadicQuery:\n\t\t\tq1.Toplevel = q.Toplevel\n\t\t\terr = q1.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tdefault:\n\t\t\terr = q.Queries[0].WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tif !q.Toplevel {\n\t\tbuf.WriteString(\"(\")\n\t}\n\tfor i, query := range q.Queries {\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\" \" + string(q.Operator) + \" \")\n\t\t}\n\t\tif query == nil {\n\t\t\treturn fmt.Errorf(\"query #%d is nil\", i+1)\n\t\t}\n\t\terr = query.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"query #%d: %w\", i+1, err)\n\t\t}\n\t}\n\tif !q.Toplevel {\n\t\tbuf.WriteString(\")\")\n\t}\n\treturn nil\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q VariadicQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn q, false\n}\n\n// GetFetchableFields implements the Query interface.\nfunc (q VariadicQuery) GetFetchableFields() []Field {\n\treturn nil\n}\n\n// GetDialect returns the SQL dialect of the VariadicQuery.\nfunc (q VariadicQuery) GetDialect() string {\n\tif len(q.Queries) == 0 {\n\t\treturn \"\"\n\t}\n\tq1 := q.Queries[0]\n\tif q1 == nil {\n\t\treturn \"\"\n\t}\n\treturn q1.GetDialect()\n}\n"
  },
  {
    "path": "cte_test.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n)\n\nfunc TestCTE(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tcte := NewCTE(\"cte\", []string{\"n\"}, Queryf(\"SELECT 1\")).Materialized().NotMaterialized().As(\"c\")\n\t\tTestTable{item: cte, wantQuery: \"cte\"}.assert(t)\n\t\tfield := NewAnyField(\"ff\", TableStruct{name: \"cte\", alias: \"c\"})\n\t\tif diff := testutil.Diff(cte.Field(\"ff\"), field); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif diff := testutil.Diff(cte.materialized, sql.NullBool{Valid: true, Bool: false}); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif diff := testutil.Diff(cte.GetAlias(), \"c\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n}\n\nfunc TestCTEs(t *testing.T) {\n\ttype TT struct {\n\t\tdescription string\n\t\tdialect     string\n\t\tctes        []CTE\n\t\twantQuery   string\n\t\twantArgs    []any\n\t\twantParams  map[string][]int\n\t}\n\n\ttests := []TT{{\n\t\tdescription: \"basic\",\n\t\tctes:        []CTE{NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))},\n\t\twantQuery:   \"WITH cte AS (SELECT 1) \",\n\t}, {\n\t\tdescription: \"recursive\",\n\t\tctes: []CTE{\n\t\t\tNewCTE(\"cte\", nil, Queryf(\"SELECT 1\")),\n\t\t\tNewRecursiveCTE(\"nums\", []string{\"n\"}, Union(\n\t\t\t\tQueryf(\"SELECT 1\"),\n\t\t\t\tQueryf(\"SELECT n+1 FROM nums WHERE n < 10\"),\n\t\t\t)),\n\t\t},\n\t\twantQuery: \"WITH RECURSIVE cte AS (SELECT 1)\" +\n\t\t\t\", nums (n) AS (SELECT 1 UNION SELECT n+1 FROM nums WHERE n < 10) \",\n\t}, {\n\t\tdescription: \"mysql materialized\",\n\t\tdialect:     DialectMySQL,\n\t\tctes:        []CTE{NewCTE(\"cte\", nil, Queryf(\"SELECT 1\")).Materialized()},\n\t\twantQuery:   \"WITH cte AS (SELECT 1) \",\n\t}, {\n\t\tdescription: \"postgres materialized\",\n\t\tdialect:     DialectPostgres,\n\t\tctes:        []CTE{NewCTE(\"cte\", nil, Queryf(\"SELECT 1\")).Materialized()},\n\t\twantQuery:   \"WITH cte AS MATERIALIZED (SELECT 1) \",\n\t}, {\n\t\tdescription: \"postgres not materialized\",\n\t\tdialect:     DialectPostgres,\n\t\tctes:        []CTE{NewCTE(\"cte\", nil, Queryf(\"SELECT 1\")).NotMaterialized()},\n\t\twantQuery:   \"WITH cte AS NOT MATERIALIZED (SELECT 1) \",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tbuf, args, params := bufpool.Get().(*bytes.Buffer), &[]any{}, make(map[string][]int)\n\t\t\tbuf.Reset()\n\t\t\tdefer bufpool.Put(buf)\n\t\t\terr := writeCTEs(context.Background(), tt.dialect, buf, args, params, tt.ctes)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(buf.String(), tt.wantQuery); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(*args, tt.wantArgs); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(params, tt.wantParams); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"invalid cte\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tbuf, args, params := bufpool.Get().(*bytes.Buffer), &[]any{}, make(map[string][]int)\n\t\tbuf.Reset()\n\t\tdefer bufpool.Put(buf)\n\t\t// no name\n\t\terr := writeCTEs(context.Background(), \"\", buf, args, params, []CTE{\n\t\t\tNewCTE(\"\", nil, Queryf(\"SELECT 1\")),\n\t\t})\n\t\tif err == nil {\n\t\t\tt.Fatal(testutil.Callers(), \"expected error but got nil\")\n\t\t}\n\t\t// no query\n\t\terr = writeCTEs(context.Background(), \"\", buf, args, params, []CTE{\n\t\t\tNewCTE(\"cte\", nil, nil),\n\t\t})\n\t\tif err == nil {\n\t\t\tt.Fatal(testutil.Callers(), \"expected error but got nil\")\n\t\t}\n\t})\n\n\tt.Run(\"err\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tbuf, args, params := bufpool.Get().(*bytes.Buffer), &[]any{}, make(map[string][]int)\n\t\tbuf.Reset()\n\t\tdefer bufpool.Put(buf)\n\t\t// VariadicQuery\n\t\terr := writeCTEs(context.Background(), \"\", buf, args, params, []CTE{\n\t\t\tNewCTE(\"cte\", nil, Union(\n\t\t\t\tQueryf(\"SELECT 1\"),\n\t\t\t\tQueryf(\"SELECT {}\", FaultySQL{}),\n\t\t\t)),\n\t\t})\n\t\tif !errors.Is(err, ErrFaultySQL) {\n\t\t\tt.Errorf(testutil.Callers()+\"expected error %q but got %q\", ErrFaultySQL, err)\n\t\t}\n\t\t// Query\n\t\terr = writeCTEs(context.Background(), \"\", buf, args, params, []CTE{\n\t\t\tNewCTE(\"cte\", nil, Queryf(\"SELECT {}\", FaultySQL{})),\n\t\t})\n\t\tif !errors.Is(err, ErrFaultySQL) {\n\t\t\tt.Errorf(testutil.Callers()+\"expected error %q but got %q\", ErrFaultySQL, err)\n\t\t}\n\t})\n}\n\nfunc TestVariadicQuery(t *testing.T) {\n\tq1, q2, q3 := Queryf(\"SELECT 1\"), Queryf(\"SELECT 2\"), Queryf(\"SELECT 3\")\n\ttests := []TestTable{{\n\t\tdescription: \"Union\",\n\t\titem:        Union(q1, q2, q3),\n\t\twantQuery:   \"(SELECT 1 UNION SELECT 2 UNION SELECT 3)\",\n\t}, {\n\t\tdescription: \"UnionAll\",\n\t\titem:        UnionAll(q1, q2, q3),\n\t\twantQuery:   \"(SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3)\",\n\t}, {\n\t\tdescription: \"Intersect\",\n\t\titem:        Intersect(q1, q2, q3),\n\t\twantQuery:   \"(SELECT 1 INTERSECT SELECT 2 INTERSECT SELECT 3)\",\n\t}, {\n\t\tdescription: \"IntersectAll\",\n\t\titem:        IntersectAll(q1, q2, q3),\n\t\twantQuery:   \"(SELECT 1 INTERSECT ALL SELECT 2 INTERSECT ALL SELECT 3)\",\n\t}, {\n\t\tdescription: \"Except\",\n\t\titem:        Except(q1, q2, q3),\n\t\twantQuery:   \"(SELECT 1 EXCEPT SELECT 2 EXCEPT SELECT 3)\",\n\t}, {\n\t\tdescription: \"ExceptAll\",\n\t\titem:        ExceptAll(q1, q2, q3),\n\t\twantQuery:   \"(SELECT 1 EXCEPT ALL SELECT 2 EXCEPT ALL SELECT 3)\",\n\t}, {\n\t\tdescription: \"No operator specified\",\n\t\titem:        VariadicQuery{Queries: []Query{q1, q2, q3}},\n\t\twantQuery:   \"(SELECT 1 UNION SELECT 2 UNION SELECT 3)\",\n\t}, {\n\t\tdescription: \"nested VariadicQuery\",\n\t\titem:        Union(Union(Union(q1, q2, q3))),\n\t\twantQuery:   \"(SELECT 1 UNION SELECT 2 UNION SELECT 3)\",\n\t}, {\n\t\tdescription: \"1 query\",\n\t\titem:        Union(q1),\n\t\twantQuery:   \"SELECT 1\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n\n\tt.Run(\"invalid VariadicQuery\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t// empty\n\t\tTestTable{item: Union()}.assertNotOK(t)\n\t\t// nil query\n\t\tTestTable{item: Union(nil)}.assertNotOK(t)\n\t\t// nil query\n\t\tTestTable{item: Union(q1, q2, nil)}.assertNotOK(t)\n\t})\n\n\tt.Run(\"err\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t// VariadicQuery\n\t\tTestTable{\n\t\t\titem: Union(\n\t\t\t\tUnion(\n\t\t\t\t\tQueryf(\"SELECT 1\"),\n\t\t\t\t\tQueryf(\"SELECT {}\", FaultySQL{}),\n\t\t\t\t),\n\t\t\t),\n\t\t}.assertErr(t, ErrFaultySQL)\n\t\t// Query\n\t\tTestTable{\n\t\t\titem: Union(Queryf(\"SELECT {}\", FaultySQL{})),\n\t\t}.assertErr(t, ErrFaultySQL)\n\t})\n\n\tt.Run(\"SetFetchableFields\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t_, ok := Union().SetFetchableFields([]Field{Expr(\"f1\")})\n\t\tif ok {\n\t\t\tt.Error(testutil.Callers(), \"expected not ok but got ok\")\n\t\t}\n\t})\n\n\tt.Run(\"GetDialect\", func(t *testing.T) {\n\t\t// empty VariadicQuery\n\t\tif diff := testutil.Diff(Union().GetDialect(), \"\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t// nil query\n\t\tif diff := testutil.Diff(Union(nil).GetDialect(), \"\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t// empty dialect propagated\n\t\tif diff := testutil.Diff(Union(Queryf(\"SELECT 1\")).GetDialect(), \"\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "delete_query.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n)\n\n// DeleteQuery represents an SQL DELETE query.\ntype DeleteQuery struct {\n\tDialect string\n\t// WITH\n\tCTEs []CTE\n\t// DELETE FROM\n\tDeleteTable  Table\n\tDeleteTables []Table\n\t// USING\n\tUsingTable Table\n\tJoinTables []JoinTable\n\t// WHERE\n\tWherePredicate Predicate\n\t// ORDER BY\n\tOrderByFields Fields\n\t// LIMIT\n\tLimitRows any\n\t// OFFSET\n\tOffsetRows any\n\t// RETURNING\n\tReturningFields []Field\n}\n\nvar _ Query = (*DeleteQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q DeleteQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\t// Table Policies\n\tvar policies []Predicate\n\tpolicies, err = appendPolicy(ctx, dialect, policies, q.DeleteTable)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"DELETE FROM %s Policy: %w\", toString(q.Dialect, q.DeleteTable), err)\n\t}\n\tpolicies, err = appendPolicy(ctx, dialect, policies, q.UsingTable)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"USING %s Policy: %w\", toString(q.Dialect, q.UsingTable), err)\n\t}\n\tfor _, joinTable := range q.JoinTables {\n\t\tpolicies, err = appendPolicy(ctx, dialect, policies, joinTable.Table)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s %s Policy: %w\", joinTable.JoinOperator, joinTable.Table, err)\n\t\t}\n\t}\n\tif len(policies) > 0 {\n\t\tif q.WherePredicate != nil {\n\t\t\tpolicies = append(policies, q.WherePredicate)\n\t\t}\n\t\tq.WherePredicate = And(policies...)\n\t}\n\t// WITH\n\tif len(q.CTEs) > 0 {\n\t\terr = writeCTEs(ctx, dialect, buf, args, params, q.CTEs)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"WITH: %w\", err)\n\t\t}\n\t}\n\t// DELETE FROM\n\tif (dialect == DialectMySQL || dialect == DialectSQLServer) && len(q.DeleteTables) > 0 {\n\t\tbuf.WriteString(\"DELETE \")\n\t\tif len(q.DeleteTables) > 1 && dialect != DialectMySQL {\n\t\t\treturn fmt.Errorf(\"dialect %q does not support multi-table DELETE\", dialect)\n\t\t}\n\t\tfor i, table := range q.DeleteTables {\n\t\t\tif i > 0 {\n\t\t\t\tbuf.WriteString(\", \")\n\t\t\t}\n\t\t\tif alias := getAlias(table); alias != \"\" {\n\t\t\t\tbuf.WriteString(alias)\n\t\t\t} else {\n\t\t\t\terr = table.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"table #%d: %w\", i+1, err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tbuf.WriteString(\"DELETE FROM \")\n\t\tif q.DeleteTable == nil {\n\t\t\treturn fmt.Errorf(\"no table provided to DELETE FROM\")\n\t\t}\n\t\terr = q.DeleteTable.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"DELETE FROM: %w\", err)\n\t\t}\n\t\tif dialect != DialectSQLServer {\n\t\t\tif alias := getAlias(q.DeleteTable); alias != \"\" {\n\t\t\t\tbuf.WriteString(\" AS \" + QuoteIdentifier(dialect, alias))\n\t\t\t}\n\t\t}\n\t}\n\tif q.UsingTable != nil || len(q.JoinTables) > 0 {\n\t\tif dialect != DialectPostgres && dialect != DialectMySQL && dialect != DialectSQLServer {\n\t\t\treturn fmt.Errorf(\"%s DELETE does not support JOIN\", dialect)\n\t\t}\n\t}\n\t// OUTPUT\n\tif len(q.ReturningFields) > 0 && dialect == DialectSQLServer {\n\t\tbuf.WriteString(\" OUTPUT \")\n\t\terr = writeFieldsWithPrefix(ctx, dialect, buf, args, params, q.ReturningFields, \"DELETED\", true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t// USING/FROM\n\tif q.UsingTable != nil {\n\t\tswitch dialect {\n\t\tcase DialectPostgres:\n\t\t\tbuf.WriteString(\" USING \")\n\t\t\terr = q.UsingTable.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"USING: %w\", err)\n\t\t\t}\n\t\tcase DialectMySQL, DialectSQLServer:\n\t\t\tbuf.WriteString(\" FROM \")\n\t\t\terr = q.UsingTable.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"FROM: %w\", err)\n\t\t\t}\n\t\t}\n\t\tif alias := getAlias(q.UsingTable); alias != \"\" {\n\t\t\tbuf.WriteString(\" AS \" + QuoteIdentifier(dialect, alias))\n\t\t}\n\t}\n\t// JOIN\n\tif len(q.JoinTables) > 0 {\n\t\tif q.UsingTable == nil {\n\t\t\treturn fmt.Errorf(\"%s can't JOIN without a USING/FROM table\", dialect)\n\t\t}\n\t\tbuf.WriteString(\" \")\n\t\terr = writeJoinTables(ctx, dialect, buf, args, params, q.JoinTables)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"JOIN: %w\", err)\n\t\t}\n\t}\n\t// WHERE\n\tif q.WherePredicate != nil {\n\t\tbuf.WriteString(\" WHERE \")\n\t\tswitch predicate := q.WherePredicate.(type) {\n\t\tcase VariadicPredicate:\n\t\t\tpredicate.Toplevel = true\n\t\t\terr = predicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"WHERE: %w\", err)\n\t\t\t}\n\t\tdefault:\n\t\t\terr = q.WherePredicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"WHERE: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\t// ORDER BY\n\tif len(q.OrderByFields) > 0 {\n\t\tif dialect != DialectMySQL {\n\t\t\treturn fmt.Errorf(\"%s UPDATE does not support ORDER BY\", dialect)\n\t\t}\n\t\tbuf.WriteString(\" ORDER BY \")\n\t\terr = q.OrderByFields.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"ORDER BY: %w\", err)\n\t\t}\n\t}\n\t// LIMIT\n\tif q.LimitRows != nil {\n\t\tif dialect != DialectMySQL {\n\t\t\treturn fmt.Errorf(\"%s UPDATE does not support LIMIT\", dialect)\n\t\t}\n\t\tbuf.WriteString(\" LIMIT \")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, q.LimitRows)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"LIMIT: %w\", err)\n\t\t}\n\t}\n\t// RETURNING\n\tif len(q.ReturningFields) > 0 && dialect != DialectSQLServer {\n\t\tif dialect != DialectPostgres && dialect != DialectSQLite && dialect != DialectMySQL {\n\t\t\treturn fmt.Errorf(\"%s UPDATE does not support RETURNING\", dialect)\n\t\t}\n\t\tbuf.WriteString(\" RETURNING \")\n\t\terr = writeFields(ctx, dialect, buf, args, params, q.ReturningFields, true)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"RETURNING: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// DeleteFrom returns a new DeleteQuery.\nfunc DeleteFrom(table Table) DeleteQuery {\n\treturn DeleteQuery{DeleteTable: table}\n}\n\n// Where appends to the WherePredicate field of the DeleteQuery.\nfunc (q DeleteQuery) Where(predicates ...Predicate) DeleteQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q DeleteQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\tswitch q.Dialect {\n\tcase DialectPostgres, DialectSQLite:\n\t\tif len(q.ReturningFields) == 0 {\n\t\t\tq.ReturningFields = fields\n\t\t\treturn q, true\n\t\t}\n\t\treturn q, false\n\tdefault:\n\t\treturn q, false\n\t}\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q DeleteQuery) GetFetchableFields() []Field {\n\tswitch q.Dialect {\n\tcase DialectPostgres, DialectSQLite:\n\t\treturn q.ReturningFields\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// GetDialect implements the Query interface.\nfunc (q DeleteQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q DeleteQuery) SetDialect(dialect string) DeleteQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// SQLiteDeleteQuery represents an SQLite DELETE query.\ntype SQLiteDeleteQuery DeleteQuery\n\nvar _ Query = (*SQLiteDeleteQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q SQLiteDeleteQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn DeleteQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// DeleteFrom returns a new SQLiteDeleteQuery.\nfunc (b sqliteQueryBuilder) DeleteFrom(table Table) SQLiteDeleteQuery {\n\treturn SQLiteDeleteQuery{\n\t\tDialect:     DialectSQLite,\n\t\tCTEs:        b.ctes,\n\t\tDeleteTable: table,\n\t}\n}\n\n// Where appends to the WherePredicate field of the SQLiteDeleteQuery.\nfunc (q SQLiteDeleteQuery) Where(predicates ...Predicate) SQLiteDeleteQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// Returning appends fields to the RETURNING clause of the SQLiteDeleteQuery.\nfunc (q SQLiteDeleteQuery) Returning(fields ...Field) SQLiteDeleteQuery {\n\tq.ReturningFields = append(q.ReturningFields, fields...)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q SQLiteDeleteQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn DeleteQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q SQLiteDeleteQuery) GetFetchableFields() []Field {\n\treturn DeleteQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q SQLiteDeleteQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q SQLiteDeleteQuery) SetDialect(dialect string) SQLiteDeleteQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// PostgresDeleteQuery represents a Postgres DELETE query.\ntype PostgresDeleteQuery DeleteQuery\n\nvar _ Query = (*PostgresDeleteQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q PostgresDeleteQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn DeleteQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// DeleteFrom returns a new PostgresDeleteQuery.\nfunc (b postgresQueryBuilder) DeleteFrom(table Table) PostgresDeleteQuery {\n\treturn PostgresDeleteQuery{\n\t\tDialect:     DialectPostgres,\n\t\tCTEs:        b.ctes,\n\t\tDeleteTable: table,\n\t}\n}\n\n// Using sets the UsingTable field of the PostgresDeleteQuery.\nfunc (q PostgresDeleteQuery) Using(table Table) PostgresDeleteQuery {\n\tq.UsingTable = table\n\treturn q\n}\n\n// Join joins a new Table to the PostgresDeleteQuery.\nfunc (q PostgresDeleteQuery) Join(table Table, predicates ...Predicate) PostgresDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the PostgresDeleteQuery.\nfunc (q PostgresDeleteQuery) LeftJoin(table Table, predicates ...Predicate) PostgresDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// FullJoin full joins a new Table to the PostgresDeleteQuery.\nfunc (q PostgresDeleteQuery) FullJoin(table Table, predicates ...Predicate) PostgresDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, FullJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the PostgresDeleteQuery.\nfunc (q PostgresDeleteQuery) CrossJoin(table Table) PostgresDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the PostgresDeleteQuery with a custom join\n// operator.\nfunc (q PostgresDeleteQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) PostgresDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// JoinUsing joins a new Table to the PostgresDeleteQuery with the USING operator.\nfunc (q PostgresDeleteQuery) JoinUsing(table Table, fields ...Field) PostgresDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, JoinUsing(table, fields...))\n\treturn q\n}\n\n// Where appends to the WherePredicate field of the PostgresDeleteQuery.\nfunc (q PostgresDeleteQuery) Where(predicates ...Predicate) PostgresDeleteQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// Returning appends fields to the RETURNING clause of the PostgresDeleteQuery.\nfunc (q PostgresDeleteQuery) Returning(fields ...Field) PostgresDeleteQuery {\n\tq.ReturningFields = append(q.ReturningFields, fields...)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q PostgresDeleteQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn DeleteQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q PostgresDeleteQuery) GetFetchableFields() []Field {\n\treturn DeleteQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q PostgresDeleteQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q PostgresDeleteQuery) SetDialect(dialect string) PostgresDeleteQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// MySQLDeleteQuery represents a MySQL DELETE query.\ntype MySQLDeleteQuery DeleteQuery\n\nvar _ Query = (*MySQLDeleteQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q MySQLDeleteQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn DeleteQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// DeleteFrom returns a new MySQLDeleteQuery.\nfunc (b mysqlQueryBuilder) DeleteFrom(table Table) MySQLDeleteQuery {\n\treturn MySQLDeleteQuery{\n\t\tDialect:     DialectMySQL,\n\t\tCTEs:        b.ctes,\n\t\tDeleteTable: table,\n\t}\n}\n\n// Delete returns a new MySQLDeleteQuery.\nfunc (b mysqlQueryBuilder) Delete(tables ...Table) MySQLDeleteQuery {\n\treturn MySQLDeleteQuery{\n\t\tDialect:      DialectMySQL,\n\t\tCTEs:         b.ctes,\n\t\tDeleteTables: tables,\n\t}\n}\n\n// From sets the UsingTable of the MySQLDeleteQuery.\nfunc (q MySQLDeleteQuery) From(table Table) MySQLDeleteQuery {\n\tq.UsingTable = table\n\treturn q\n}\n\n// Join joins a new Table to the MySQLDeleteQuery.\nfunc (q MySQLDeleteQuery) Join(table Table, predicates ...Predicate) MySQLDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the MySQLDeleteQuery.\nfunc (q MySQLDeleteQuery) LeftJoin(table Table, predicates ...Predicate) MySQLDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// FullJoin full joins a new Table to the MySQLDeleteQuery.\nfunc (q MySQLDeleteQuery) FullJoin(table Table, predicates ...Predicate) MySQLDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, FullJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the MySQLDeleteQuery.\nfunc (q MySQLDeleteQuery) CrossJoin(table Table) MySQLDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the MySQLDeleteQuery with a custom join\n// operator.\nfunc (q MySQLDeleteQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) MySQLDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// JoinUsing joins a new Table to the MySQLDeleteQuery with the USING operator.\nfunc (q MySQLDeleteQuery) JoinUsing(table Table, fields ...Field) MySQLDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, JoinUsing(table, fields...))\n\treturn q\n}\n\n// Where appends to the WherePredicate field of the MySQLDeleteQuery.\nfunc (q MySQLDeleteQuery) Where(predicates ...Predicate) MySQLDeleteQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// OrderBy sets the OrderByFields field of the MySQLDeleteQuery.\nfunc (q MySQLDeleteQuery) OrderBy(fields ...Field) MySQLDeleteQuery {\n\tq.OrderByFields = append(q.OrderByFields, fields...)\n\treturn q\n}\n\n// Limit sets the LimitRows field of the MySQLDeleteQuery.\nfunc (q MySQLDeleteQuery) Limit(limit any) MySQLDeleteQuery {\n\tq.LimitRows = limit\n\treturn q\n}\n\n// Returning appends fields to the RETURNING clause of the MySQLDeleteQuery.\nfunc (q MySQLDeleteQuery) Returning(fields ...Field) MySQLDeleteQuery {\n\tq.ReturningFields = append(q.ReturningFields, fields...)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q MySQLDeleteQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn DeleteQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q MySQLDeleteQuery) GetFetchableFields() []Field {\n\treturn DeleteQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q MySQLDeleteQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q MySQLDeleteQuery) SetDialect(dialect string) MySQLDeleteQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// SQLServerDeleteQuery represents an SQL Server DELETE query.\ntype SQLServerDeleteQuery DeleteQuery\n\nvar _ Query = (*SQLServerDeleteQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q SQLServerDeleteQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn DeleteQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// DeleteFrom returns a new SQLServerDeleteQuery.\nfunc (b sqlserverQueryBuilder) DeleteFrom(table Table) SQLServerDeleteQuery {\n\treturn SQLServerDeleteQuery{\n\t\tDialect:     DialectSQLServer,\n\t\tCTEs:        b.ctes,\n\t\tDeleteTable: table,\n\t}\n}\n\n// Delete returns a new SQLServerDeleteQuery.\nfunc (b sqlserverQueryBuilder) Delete(table Table) SQLServerDeleteQuery {\n\treturn SQLServerDeleteQuery{\n\t\tDialect:      DialectSQLServer,\n\t\tCTEs:         b.ctes,\n\t\tDeleteTables: []Table{table},\n\t}\n}\n\n// From sets the UsingTable of the SQLServerDeleteQuery.\nfunc (q SQLServerDeleteQuery) From(table Table) SQLServerDeleteQuery {\n\tq.UsingTable = table\n\treturn q\n}\n\n// Join joins a new Table to the SQLServerDeleteQuery.\nfunc (q SQLServerDeleteQuery) Join(table Table, predicates ...Predicate) SQLServerDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the SQLServerDeleteQuery.\nfunc (q SQLServerDeleteQuery) LeftJoin(table Table, predicates ...Predicate) SQLServerDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// FullJoin full joins a new Table to the SQLServerDeleteQuery.\nfunc (q SQLServerDeleteQuery) FullJoin(table Table, predicates ...Predicate) SQLServerDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, FullJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the SQLServerDeleteQuery.\nfunc (q SQLServerDeleteQuery) CrossJoin(table Table) SQLServerDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the SQLServerDeleteQuery with a custom join\n// operator.\nfunc (q SQLServerDeleteQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) SQLServerDeleteQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// Where appends to the WherePredicate field of the SQLServerDeleteQuery.\nfunc (q SQLServerDeleteQuery) Where(predicates ...Predicate) SQLServerDeleteQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q SQLServerDeleteQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn DeleteQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q SQLServerDeleteQuery) GetFetchableFields() []Field {\n\treturn DeleteQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q SQLServerDeleteQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q SQLServerDeleteQuery) SetDialect(dialect string) SQLServerDeleteQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n"
  },
  {
    "path": "delete_query_test.go",
    "content": "package sq\n\nimport (\n\t\"testing\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n)\n\nfunc TestSQLiteDeleteQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := SQLite.DeleteFrom(a).Returning(a.FIRST_NAME).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectSQLite)\n\t\tfields := q1.GetFetchableFields()\n\t\tif diff := testutil.Diff(fields, []Field{a.FIRST_NAME}); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Delete Returning\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLite.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tDeleteFrom(a).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1)).\n\t\t\tReturning(a.FIRST_NAME, a.LAST_NAME)\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" DELETE FROM actor AS a\" +\n\t\t\t\" WHERE a.actor_id = $1\" +\n\t\t\t\" RETURNING a.first_name, a.last_name\"\n\t\ttt.wantArgs = []any{1}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestPostgresDeleteQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := Postgres.DeleteFrom(a).Returning(a.FIRST_NAME).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectPostgres)\n\t\tfields := q1.GetFetchableFields()\n\t\tif diff := testutil.Diff(fields, []Field{a.FIRST_NAME}); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Delete Returning\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tDeleteFrom(a).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1)).\n\t\t\tReturning(a.FIRST_NAME, a.LAST_NAME)\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" DELETE FROM actor AS a\" +\n\t\t\t\" WHERE a.actor_id = $1\" +\n\t\t\t\" RETURNING a.first_name, a.last_name\"\n\t\ttt.wantArgs = []any{1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"Join\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tDeleteFrom(a).\n\t\t\tUsing(a).\n\t\t\tJoin(a, Expr(\"1 = 1\")).\n\t\t\tLeftJoin(a, Expr(\"1 = 1\")).\n\t\t\tFullJoin(a, Expr(\"1 = 1\")).\n\t\t\tCrossJoin(a).\n\t\t\tCustomJoin(\",\", a).\n\t\t\tJoinUsing(a, a.FIRST_NAME, a.LAST_NAME)\n\t\ttt.wantQuery = \"DELETE FROM actor AS a\" +\n\t\t\t\" USING actor AS a\" +\n\t\t\t\" JOIN actor AS a ON 1 = 1\" +\n\t\t\t\" LEFT JOIN actor AS a ON 1 = 1\" +\n\t\t\t\" FULL JOIN actor AS a ON 1 = 1\" +\n\t\t\t\" CROSS JOIN actor AS a\" +\n\t\t\t\" , actor AS a\" +\n\t\t\t\" JOIN actor AS a USING (first_name, last_name)\"\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestMySQLDeleteQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := MySQL.DeleteFrom(a).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectMySQL)\n\t\tfields := q1.GetFetchableFields()\n\t\tif len(fields) != 0 {\n\t\t\tt.Error(testutil.Callers(), \"expected 0 fields but got %v\", fields)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Where\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tDeleteFrom(a).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1))\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" DELETE FROM actor\" +\n\t\t\t\" WHERE actor.actor_id = ?\"\n\t\ttt.wantArgs = []any{1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"OrderBy Limit\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tDeleteFrom(a).\n\t\t\tOrderBy(a.ACTOR_ID).\n\t\t\tLimit(5)\n\t\ttt.wantQuery = \"DELETE FROM actor\" +\n\t\t\t\" ORDER BY actor.actor_id\" +\n\t\t\t\" LIMIT ?\"\n\t\ttt.wantArgs = []any{5}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"Delete Returning\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tDeleteFrom(a).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1)).\n\t\t\tReturning(a.FIRST_NAME, a.LAST_NAME)\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" DELETE FROM actor\" +\n\t\t\t\" WHERE actor.actor_id = ?\" +\n\t\t\t\" RETURNING actor.first_name, actor.last_name\"\n\t\ttt.wantArgs = []any{1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"Join\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tDelete(a).\n\t\t\tFrom(a).\n\t\t\tJoin(a, Expr(\"1 = 1\")).\n\t\t\tLeftJoin(a, Expr(\"1 = 1\")).\n\t\t\tFullJoin(a, Expr(\"1 = 1\")).\n\t\t\tCrossJoin(a).\n\t\t\tCustomJoin(\",\", a).\n\t\t\tJoinUsing(a, a.FIRST_NAME, a.LAST_NAME)\n\t\ttt.wantQuery = \"DELETE actor\" +\n\t\t\t\" FROM actor\" +\n\t\t\t\" JOIN actor ON 1 = 1\" +\n\t\t\t\" LEFT JOIN actor ON 1 = 1\" +\n\t\t\t\" FULL JOIN actor ON 1 = 1\" +\n\t\t\t\" CROSS JOIN actor\" +\n\t\t\t\" , actor\" +\n\t\t\t\" JOIN actor USING (first_name, last_name)\"\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestSQLServerDeleteQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := SQLServer.DeleteFrom(a).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectSQLServer)\n\t\tq1 = q1.SetDialect(DialectMySQL)\n\t\tfields := q1.GetFetchableFields()\n\t\tif len(fields) != 0 {\n\t\t\tt.Error(testutil.Callers(), \"expected 0 fields but got %v\", fields)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Where\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLServer.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tDeleteFrom(a).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1))\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" DELETE FROM actor\" +\n\t\t\t\" WHERE actor.actor_id = @p1\"\n\t\ttt.wantArgs = []any{1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"Join\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLServer.\n\t\t\tDeleteFrom(a).\n\t\t\tFrom(a).\n\t\t\tJoin(a, Expr(\"1 = 1\")).\n\t\t\tLeftJoin(a, Expr(\"1 = 1\")).\n\t\t\tFullJoin(a, Expr(\"1 = 1\")).\n\t\t\tCrossJoin(a).\n\t\t\tCustomJoin(\",\", a)\n\t\ttt.wantQuery = \"DELETE FROM actor\" +\n\t\t\t\" FROM actor\" +\n\t\t\t\" JOIN actor ON 1 = 1\" +\n\t\t\t\" LEFT JOIN actor ON 1 = 1\" +\n\t\t\t\" FULL JOIN actor ON 1 = 1\" +\n\t\t\t\" CROSS JOIN actor\" +\n\t\t\t\" , actor\"\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestDeleteQuery(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := DeleteQuery{DeleteTable: Expr(\"tbl\"), Dialect: \"lorem ipsum\"}\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"PolicyTable\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = DeleteQuery{\n\t\t\tDeleteTable:    policyTableStub{policy: And(Expr(\"1 = 1\"), Expr(\"2 = 2\"))},\n\t\t\tWherePredicate: Expr(\"3 = 3\"),\n\t\t}\n\t\ttt.wantQuery = \"DELETE FROM policy_table_stub WHERE (1 = 1 AND 2 = 2) AND 3 = 3\"\n\t\ttt.assert(t)\n\t})\n\n\tnotOKTests := []TestTable{{\n\t\tdescription: \"nil FromTable not allowed\",\n\t\titem: DeleteQuery{\n\t\t\tDeleteTable: nil,\n\t\t},\n\t}, {\n\t\tdescription: \"sqlite does not support JOIN\",\n\t\titem: DeleteQuery{\n\t\t\tDialect:     DialectSQLite,\n\t\t\tDeleteTable: Expr(\"tbl\"),\n\t\t\tUsingTable:  Expr(\"tbl\"),\n\t\t\tJoinTables: []JoinTable{\n\t\t\t\tJoin(Expr(\"tbl\"), Expr(\"1 = 1\")),\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"postgres does not allow JOIN without USING\",\n\t\titem: DeleteQuery{\n\t\t\tDialect:     DialectPostgres,\n\t\t\tDeleteTable: Expr(\"tbl\"),\n\t\t\tJoinTables: []JoinTable{\n\t\t\t\tJoin(Expr(\"tbl\"), Expr(\"1 = 1\")),\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"dialect does not support ORDER BY\",\n\t\titem: DeleteQuery{\n\t\t\tDialect:       DialectPostgres,\n\t\t\tDeleteTable:   Expr(\"tbl\"),\n\t\t\tOrderByFields: Fields{Expr(\"f1\")},\n\t\t},\n\t}, {\n\t\tdescription: \"dialect does not support LIMIT\",\n\t\titem: DeleteQuery{\n\t\t\tDialect:     DialectPostgres,\n\t\t\tDeleteTable: Expr(\"tbl\"),\n\t\t\tLimitRows:   5,\n\t\t},\n\t}}\n\n\tfor _, tt := range notOKTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertNotOK(t)\n\t\t})\n\t}\n\n\terrTests := []TestTable{{\n\t\tdescription: \"FromTable Policy err\",\n\t\titem: DeleteQuery{\n\t\t\tDeleteTable: policyTableStub{err: ErrFaultySQL},\n\t\t},\n\t}, {\n\t\tdescription: \"UsingTable Policy err\",\n\t\titem: DeleteQuery{\n\t\t\tDeleteTable: Expr(\"tbl\"),\n\t\t\tUsingTable:  policyTableStub{err: ErrFaultySQL},\n\t\t},\n\t}, {\n\t\tdescription: \"JoinTables Policy err\",\n\t\titem: DeleteQuery{\n\t\t\tDeleteTable: Expr(\"tbl\"),\n\t\t\tUsingTable:  Expr(\"tbl\"),\n\t\t\tJoinTables: []JoinTable{\n\t\t\t\tJoin(policyTableStub{err: ErrFaultySQL}, Expr(\"1 = 1\")),\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"CTEs err\",\n\t\titem: DeleteQuery{\n\t\t\tCTEs:        []CTE{NewCTE(\"cte\", nil, Queryf(\"SELECT {}\", FaultySQL{}))},\n\t\t\tDeleteTable: Expr(\"tbl\"),\n\t\t},\n\t}, {\n\t\tdescription: \"FromTable err\",\n\t\titem: DeleteQuery{\n\t\t\tDeleteTable: FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"postgres UsingTable err\",\n\t\titem: DeleteQuery{\n\t\t\tDialect:     DialectPostgres,\n\t\t\tDeleteTable: Expr(\"tbl\"),\n\t\t\tUsingTable:  FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"sqlserver UsingTable err\",\n\t\titem: DeleteQuery{\n\t\t\tDialect:     DialectSQLServer,\n\t\t\tDeleteTable: Expr(\"tbl\"),\n\t\t\tUsingTable:  FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"JoinTables err\",\n\t\titem: DeleteQuery{\n\t\t\tDialect:     DialectPostgres,\n\t\t\tDeleteTable: Expr(\"tbl\"),\n\t\t\tUsingTable:  Expr(\"tbl\"),\n\t\t\tJoinTables: []JoinTable{\n\t\t\t\tJoin(Expr(\"tbl\"), FaultySQL{}),\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"WherePredicate Variadic err\",\n\t\titem: DeleteQuery{\n\t\t\tDeleteTable:    Expr(\"tbl\"),\n\t\t\tWherePredicate: And(FaultySQL{}),\n\t\t},\n\t}, {\n\t\tdescription: \"WherePredicate err\",\n\t\titem: DeleteQuery{\n\t\t\tDeleteTable:    Expr(\"tbl\"),\n\t\t\tWherePredicate: FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"OrderByFields err\",\n\t\titem: DeleteQuery{\n\t\t\tDialect:       DialectMySQL,\n\t\t\tDeleteTable:   Expr(\"tbl\"),\n\t\t\tOrderByFields: Fields{FaultySQL{}},\n\t\t},\n\t}, {\n\t\tdescription: \"LimitRows err\",\n\t\titem: DeleteQuery{\n\t\t\tDialect:       DialectMySQL,\n\t\t\tDeleteTable:   Expr(\"tbl\"),\n\t\t\tOrderByFields: Fields{Expr(\"f1\")},\n\t\t\tLimitRows:     FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"ReturningFields err\",\n\t\titem: DeleteQuery{\n\t\t\tDialect:         DialectPostgres,\n\t\t\tDeleteTable:     Expr(\"tbl\"),\n\t\t\tReturningFields: Fields{FaultySQL{}},\n\t\t},\n\t}}\n\n\tfor _, tt := range errTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertErr(t, ErrFaultySQL)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "fetch_exec.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\n// Default dialect used by all queries (if no dialect is explicitly provided).\nvar DefaultDialect atomic.Pointer[string]\n\n// A Cursor represents a database cursor.\ntype Cursor[T any] struct {\n\tctx           context.Context\n\trow           *Row\n\trowmapper     func(*Row) T\n\tqueryStats    QueryStats\n\tlogSettings   LogSettings\n\tlogger        SqLogger\n\tlogged        int32\n\tfieldNames    []string\n\tresultsBuffer *bytes.Buffer\n}\n\n// FetchCursor returns a new cursor.\nfunc FetchCursor[T any](db DB, query Query, rowmapper func(*Row) T) (*Cursor[T], error) {\n\treturn fetchCursor(context.Background(), db, query, rowmapper, 1)\n}\n\n// FetchCursorContext is like FetchCursor but additionally requires a context.Context.\nfunc FetchCursorContext[T any](ctx context.Context, db DB, query Query, rowmapper func(*Row) T) (*Cursor[T], error) {\n\treturn fetchCursor(ctx, db, query, rowmapper, 1)\n}\n\nfunc fetchCursor[T any](ctx context.Context, db DB, query Query, rowmapper func(*Row) T, skip int) (cursor *Cursor[T], err error) {\n\tif db == nil {\n\t\treturn nil, fmt.Errorf(\"db is nil\")\n\t}\n\tif query == nil {\n\t\treturn nil, fmt.Errorf(\"query is nil\")\n\t}\n\tif rowmapper == nil {\n\t\treturn nil, fmt.Errorf(\"rowmapper is nil\")\n\t}\n\tdialect := query.GetDialect()\n\tif dialect == \"\" {\n\t\tdefaultDialect := DefaultDialect.Load()\n\t\tif defaultDialect != nil {\n\t\t\tdialect = *defaultDialect\n\t\t}\n\t}\n\t// If we can't set the fetchable fields, the query is static.\n\t_, ok := query.SetFetchableFields(nil)\n\tcursor = &Cursor[T]{\n\t\tctx:       ctx,\n\t\trowmapper: rowmapper,\n\t\trow: &Row{\n\t\t\tdialect:       dialect,\n\t\t\tqueryIsStatic: !ok,\n\t\t},\n\t\tqueryStats: QueryStats{\n\t\t\tDialect:  dialect,\n\t\t\tParams:   make(map[string][]int),\n\t\t\tRowCount: sql.NullInt64{Valid: true},\n\t\t},\n\t}\n\n\t// If the query is dynamic, call the rowmapper to populate row.fields and\n\t// row.scanDest. Then, insert those fields back into the query.\n\tif !cursor.row.queryIsStatic {\n\t\tdefer mapperFunctionPanicked(&err)\n\t\t_ = cursor.rowmapper(cursor.row)\n\t\tquery, _ = query.SetFetchableFields(cursor.row.fields)\n\t}\n\n\t// Build query.\n\tbuf := bufpool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer bufpool.Put(buf)\n\terr = query.WriteSQL(ctx, dialect, buf, &cursor.queryStats.Args, cursor.queryStats.Params)\n\tcursor.queryStats.Query = buf.String()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Setup logger.\n\tcursor.logger, _ = db.(SqLogger)\n\tif cursor.logger == nil {\n\t\tlogQuery, _ := defaultLogQuery.Load().(func(context.Context, QueryStats))\n\t\tif logQuery != nil {\n\t\t\tlogSettings, _ := defaultLogSettings.Load().(func(context.Context, *LogSettings))\n\t\t\tcursor.logger = &sqLogStruct{\n\t\t\t\tlogSettings: logSettings,\n\t\t\t\tlogQuery:    logQuery,\n\t\t\t}\n\t\t}\n\t}\n\tif cursor.logger != nil {\n\t\tcursor.logger.SqLogSettings(ctx, &cursor.logSettings)\n\t\tif cursor.logSettings.IncludeCaller {\n\t\t\tcursor.queryStats.CallerFile, cursor.queryStats.CallerLine, cursor.queryStats.CallerFunction = caller(skip + 1)\n\t\t}\n\t}\n\n\t// Run query.\n\tif cursor.logSettings.IncludeTime {\n\t\tcursor.queryStats.StartedAt = time.Now()\n\t}\n\tcursor.row.sqlRows, cursor.queryStats.Err = db.QueryContext(ctx, cursor.queryStats.Query, cursor.queryStats.Args...)\n\tif cursor.logSettings.IncludeTime {\n\t\tcursor.queryStats.TimeTaken = time.Since(cursor.queryStats.StartedAt)\n\t}\n\tif cursor.queryStats.Err != nil {\n\t\tcursor.log()\n\t\treturn nil, cursor.queryStats.Err\n\t}\n\n\t// If the query is static, we now know the number of columns returned by\n\t// the query and can allocate the values slice and scanDest slice for\n\t// scanning later.\n\tif cursor.row.queryIsStatic {\n\t\tcursor.row.columns, err = cursor.row.sqlRows.Columns()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcursor.row.columnTypes, err = cursor.row.sqlRows.ColumnTypes()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcursor.row.columnIndex = make(map[string]int)\n\t\tfor index, column := range cursor.row.columns {\n\t\t\tcursor.row.columnIndex[column] = index\n\t\t}\n\t\tcursor.row.values = make([]any, len(cursor.row.columns))\n\t\tcursor.row.scanDest = make([]any, len(cursor.row.columns))\n\t\tfor index := range cursor.row.values {\n\t\t\tcursor.row.scanDest[index] = &cursor.row.values[index]\n\t\t}\n\t}\n\n\t// Allocate the resultsBuffer.\n\tif cursor.logSettings.IncludeResults > 0 {\n\t\tcursor.resultsBuffer = bufpool.Get().(*bytes.Buffer)\n\t\tcursor.resultsBuffer.Reset()\n\t}\n\treturn cursor, nil\n}\n\n// Next advances the cursor to the next result.\nfunc (cursor *Cursor[T]) Next() bool {\n\thasNext := cursor.row.sqlRows.Next()\n\tif hasNext {\n\t\tcursor.queryStats.RowCount.Int64++\n\t} else {\n\t\tcursor.log()\n\t}\n\treturn hasNext\n}\n\n// RowCount returns the current row number so far.\nfunc (cursor *Cursor[T]) RowCount() int64 { return cursor.queryStats.RowCount.Int64 }\n\n// Result returns the cursor result.\nfunc (cursor *Cursor[T]) Result() (result T, err error) {\n\terr = cursor.row.sqlRows.Scan(cursor.row.scanDest...)\n\tif err != nil {\n\t\tcursor.log()\n\t\tfieldMappings := getFieldMappings(cursor.queryStats.Dialect, cursor.row.fields, cursor.row.scanDest)\n\t\treturn result, fmt.Errorf(\"please check if your mapper function is correct:%s\\n%w\", fieldMappings, err)\n\t}\n\t// If results should be logged, write the row into the resultsBuffer.\n\tif cursor.resultsBuffer != nil && cursor.queryStats.RowCount.Int64 <= int64(cursor.logSettings.IncludeResults) {\n\t\tif len(cursor.fieldNames) == 0 {\n\t\t\tcursor.fieldNames = getFieldNames(cursor.ctx, cursor.row)\n\t\t}\n\t\tcursor.resultsBuffer.WriteString(\"\\n----[ Row \" + strconv.FormatInt(cursor.queryStats.RowCount.Int64, 10) + \" ]----\")\n\t\tfor i := range cursor.row.scanDest {\n\t\t\tcursor.resultsBuffer.WriteString(\"\\n\")\n\t\t\tif i < len(cursor.fieldNames) {\n\t\t\t\tcursor.resultsBuffer.WriteString(cursor.fieldNames[i])\n\t\t\t}\n\t\t\tcursor.resultsBuffer.WriteString(\": \")\n\t\t\tscanDest := cursor.row.scanDest[i]\n\t\t\trhs, err := Sprint(cursor.queryStats.Dialect, scanDest)\n\t\t\tif err != nil {\n\t\t\t\tcursor.resultsBuffer.WriteString(\"%!(error=\" + err.Error() + \")\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcursor.resultsBuffer.WriteString(rhs)\n\t\t}\n\t}\n\tcursor.row.runningIndex = 0\n\tdefer mapperFunctionPanicked(&err)\n\tresult = cursor.rowmapper(cursor.row)\n\treturn result, nil\n}\n\nfunc (cursor *Cursor[T]) log() {\n\tif !atomic.CompareAndSwapInt32(&cursor.logged, 0, 1) {\n\t\treturn\n\t}\n\tif cursor.resultsBuffer != nil {\n\t\tcursor.queryStats.Results = cursor.resultsBuffer.String()\n\t\tbufpool.Put(cursor.resultsBuffer)\n\t}\n\tif cursor.logger == nil {\n\t\treturn\n\t}\n\tif cursor.logSettings.LogAsynchronously {\n\t\tgo cursor.logger.SqLogQuery(cursor.ctx, cursor.queryStats)\n\t} else {\n\t\tcursor.logger.SqLogQuery(cursor.ctx, cursor.queryStats)\n\t}\n}\n\n// Close closes the cursor.\nfunc (cursor *Cursor[T]) Close() error {\n\tcursor.log()\n\tif err := cursor.row.sqlRows.Close(); err != nil {\n\t\treturn err\n\t}\n\tif err := cursor.row.sqlRows.Err(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// FetchOne returns the first result from running the given Query on the given\n// DB.\nfunc FetchOne[T any](db DB, query Query, rowmapper func(*Row) T) (T, error) {\n\tcursor, err := fetchCursor(context.Background(), db, query, rowmapper, 1)\n\tif err != nil {\n\t\treturn *new(T), err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResult(cursor)\n}\n\n// FetchOneContext is like FetchOne but additionally requires a context.Context.\nfunc FetchOneContext[T any](ctx context.Context, db DB, query Query, rowmapper func(*Row) T) (T, error) {\n\tcursor, err := fetchCursor(ctx, db, query, rowmapper, 1)\n\tif err != nil {\n\t\treturn *new(T), err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResult(cursor)\n}\n\n// FetchAll returns all results from running the given Query on the given DB.\nfunc FetchAll[T any](db DB, query Query, rowmapper func(*Row) T) ([]T, error) {\n\tcursor, err := fetchCursor(context.Background(), db, query, rowmapper, 1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResults(cursor)\n}\n\n// FetchAllContext is like FetchAll but additionally requires a context.Context.\nfunc FetchAllContext[T any](ctx context.Context, db DB, query Query, rowmapper func(*Row) T) ([]T, error) {\n\tcursor, err := fetchCursor(ctx, db, query, rowmapper, 1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResults(cursor)\n}\n\n// CompiledFetch is the result of compiling a Query down into a query string\n// and args slice. A CompiledFetch can be safely executed in parallel.\ntype CompiledFetch[T any] struct {\n\tdialect   string\n\tquery     string\n\targs      []any\n\tparams    map[string][]int\n\trowmapper func(*Row) T\n\t// if queryIsStatic is true, the rowmapper doesn't actually know what\n\t// columns are in the query and it must be determined at runtime after\n\t// running the query.\n\tqueryIsStatic bool\n}\n\n// NewCompiledFetch returns a new CompiledFetch.\nfunc NewCompiledFetch[T any](dialect string, query string, args []any, params map[string][]int, rowmapper func(*Row) T) *CompiledFetch[T] {\n\treturn &CompiledFetch[T]{\n\t\tdialect:   dialect,\n\t\tquery:     query,\n\t\targs:      args,\n\t\tparams:    params,\n\t\trowmapper: rowmapper,\n\t}\n}\n\n// CompileFetch returns a new CompileFetch.\nfunc CompileFetch[T any](q Query, rowmapper func(*Row) T) (*CompiledFetch[T], error) {\n\treturn CompileFetchContext(context.Background(), q, rowmapper)\n}\n\n// CompileFetchContext is like CompileFetch but accepts a context.Context.\nfunc CompileFetchContext[T any](ctx context.Context, query Query, rowmapper func(*Row) T) (compiledFetch *CompiledFetch[T], err error) {\n\tif query == nil {\n\t\treturn nil, fmt.Errorf(\"query is nil\")\n\t}\n\tif rowmapper == nil {\n\t\treturn nil, fmt.Errorf(\"rowmapper is nil\")\n\t}\n\tdialect := query.GetDialect()\n\tif dialect == \"\" {\n\t\tdefaultDialect := DefaultDialect.Load()\n\t\tif defaultDialect != nil {\n\t\t\tdialect = *defaultDialect\n\t\t}\n\t}\n\t// If we can't set the fetchable fields, the query is static.\n\t_, ok := query.SetFetchableFields(nil)\n\tcompiledFetch = &CompiledFetch[T]{\n\t\tdialect:       dialect,\n\t\tparams:        make(map[string][]int),\n\t\trowmapper:     rowmapper,\n\t\tqueryIsStatic: !ok,\n\t}\n\trow := &Row{\n\t\tdialect:       dialect,\n\t\tqueryIsStatic: !ok,\n\t}\n\n\t// If the query is dynamic, call the rowmapper to populate row.fields.\n\t// Then, insert those fields back into the query.\n\tif !row.queryIsStatic {\n\t\tdefer mapperFunctionPanicked(&err)\n\t\t_ = rowmapper(row)\n\t\tquery, _ = query.SetFetchableFields(row.fields)\n\t}\n\n\t// Build query.\n\tbuf := bufpool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer bufpool.Put(buf)\n\terr = query.WriteSQL(ctx, dialect, buf, &compiledFetch.args, compiledFetch.params)\n\tcompiledFetch.query = buf.String()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn compiledFetch, nil\n}\n\n// FetchCursor returns a new cursor.\nfunc (compiledFetch *CompiledFetch[T]) FetchCursor(db DB, params Params) (*Cursor[T], error) {\n\treturn compiledFetch.fetchCursor(context.Background(), db, params, 1)\n}\n\n// FetchCursorContext is like FetchCursor but additionally requires a context.Context.\nfunc (compiledFetch *CompiledFetch[T]) FetchCursorContext(ctx context.Context, db DB, params Params) (*Cursor[T], error) {\n\treturn compiledFetch.fetchCursor(ctx, db, params, 1)\n}\n\nfunc (compiledFetch *CompiledFetch[T]) fetchCursor(ctx context.Context, db DB, params Params, skip int) (cursor *Cursor[T], err error) {\n\tif db == nil {\n\t\treturn nil, fmt.Errorf(\"db is nil\")\n\t}\n\tcursor = &Cursor[T]{\n\t\tctx:       ctx,\n\t\trowmapper: compiledFetch.rowmapper,\n\t\trow: &Row{\n\t\t\tdialect:       compiledFetch.dialect,\n\t\t\tqueryIsStatic: compiledFetch.queryIsStatic,\n\t\t},\n\t\tqueryStats: QueryStats{\n\t\t\tDialect: compiledFetch.dialect,\n\t\t\tQuery:   compiledFetch.query,\n\t\t\tArgs:    compiledFetch.args,\n\t\t\tParams:  compiledFetch.params,\n\t\t},\n\t}\n\n\t// Call the rowmapper to populate row.scanDest.\n\tif !cursor.row.queryIsStatic {\n\t\tdefer mapperFunctionPanicked(&err)\n\t\t_ = cursor.rowmapper(cursor.row)\n\t}\n\n\t// Substitute params.\n\tcursor.queryStats.Args, err = substituteParams(cursor.queryStats.Dialect, cursor.queryStats.Args, cursor.queryStats.Params, params)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Setup logger.\n\tcursor.queryStats.RowCount.Valid = true\n\tcursor.logger, _ = db.(SqLogger)\n\tif cursor.logger == nil {\n\t\tlogQuery, _ := defaultLogQuery.Load().(func(context.Context, QueryStats))\n\t\tif logQuery != nil {\n\t\t\tlogSettings, _ := defaultLogSettings.Load().(func(context.Context, *LogSettings))\n\t\t\tcursor.logger = &sqLogStruct{\n\t\t\t\tlogSettings: logSettings,\n\t\t\t\tlogQuery:    logQuery,\n\t\t\t}\n\t\t}\n\t}\n\tif cursor.logger != nil {\n\t\tcursor.logger.SqLogSettings(ctx, &cursor.logSettings)\n\t\tif cursor.logSettings.IncludeCaller {\n\t\t\tcursor.queryStats.CallerFile, cursor.queryStats.CallerLine, cursor.queryStats.CallerFunction = caller(skip + 1)\n\t\t}\n\t}\n\n\t// Run query.\n\tif cursor.logSettings.IncludeTime {\n\t\tcursor.queryStats.StartedAt = time.Now()\n\t}\n\tcursor.row.sqlRows, cursor.queryStats.Err = db.QueryContext(ctx, cursor.queryStats.Query, cursor.queryStats.Args...)\n\tif cursor.logSettings.IncludeTime {\n\t\tcursor.queryStats.TimeTaken = time.Since(cursor.queryStats.StartedAt)\n\t}\n\tif cursor.queryStats.Err != nil {\n\t\treturn nil, cursor.queryStats.Err\n\t}\n\n\t// If the query is static, we now know the number of columns returned by\n\t// the query and can allocate the values slice and scanDest slice for\n\t// scanning later.\n\tif cursor.row.queryIsStatic {\n\t\tcursor.row.columns, err = cursor.row.sqlRows.Columns()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcursor.row.columnTypes, err = cursor.row.sqlRows.ColumnTypes()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcursor.row.columnIndex = make(map[string]int)\n\t\tfor index, column := range cursor.row.columns {\n\t\t\tcursor.row.columnIndex[column] = index\n\t\t}\n\t\tcursor.row.values = make([]any, len(cursor.row.columns))\n\t\tcursor.row.scanDest = make([]any, len(cursor.row.columns))\n\t\tfor index := range cursor.row.values {\n\t\t\tcursor.row.scanDest[index] = &cursor.row.values[index]\n\t\t}\n\t}\n\n\t// Allocate the resultsBuffer.\n\tif cursor.logSettings.IncludeResults > 0 {\n\t\tcursor.resultsBuffer = bufpool.Get().(*bytes.Buffer)\n\t\tcursor.resultsBuffer.Reset()\n\t}\n\treturn cursor, nil\n}\n\n// FetchOne returns the first result from running the CompiledFetch on the\n// given DB with the give params.\nfunc (compiledFetch *CompiledFetch[T]) FetchOne(db DB, params Params) (T, error) {\n\tcursor, err := compiledFetch.fetchCursor(context.Background(), db, params, 1)\n\tif err != nil {\n\t\treturn *new(T), err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResult(cursor)\n}\n\n// FetchOneContext is like FetchOne but additionally requires a context.Context.\nfunc (compiledFetch *CompiledFetch[T]) FetchOneContext(ctx context.Context, db DB, params Params) (T, error) {\n\tcursor, err := compiledFetch.fetchCursor(ctx, db, params, 1)\n\tif err != nil {\n\t\treturn *new(T), err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResult(cursor)\n}\n\n// FetchAll returns all the results from running the CompiledFetch on the given\n// DB with the give params.\nfunc (compiledFetch *CompiledFetch[T]) FetchAll(db DB, params Params) ([]T, error) {\n\tcursor, err := compiledFetch.fetchCursor(context.Background(), db, params, 1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResults(cursor)\n}\n\n// FetchAllContext is like FetchAll but additionally requires a context.Context.\nfunc (compiledFetch *CompiledFetch[T]) FetchAllContext(ctx context.Context, db DB, params Params) ([]T, error) {\n\tcursor, err := compiledFetch.fetchCursor(ctx, db, params, 1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResults(cursor)\n}\n\n// GetSQL returns a copy of the dialect, query, args, params and rowmapper that\n// make up the CompiledFetch.\nfunc (compiledFetch *CompiledFetch[T]) GetSQL() (dialect string, query string, args []any, params map[string][]int, rowmapper func(*Row) T) {\n\tdialect = compiledFetch.dialect\n\tquery = compiledFetch.query\n\targs = make([]any, len(compiledFetch.args))\n\tparams = make(map[string][]int)\n\tcopy(args, compiledFetch.args)\n\tfor name, indexes := range compiledFetch.params {\n\t\tindexes2 := make([]int, len(indexes))\n\t\tcopy(indexes2, indexes)\n\t\tparams[name] = indexes2\n\t}\n\treturn dialect, query, args, params, compiledFetch.rowmapper\n}\n\n// Prepare creates a PreparedFetch from a CompiledFetch by preparing it on\n// the given DB.\nfunc (compiledFetch *CompiledFetch[T]) Prepare(db DB) (*PreparedFetch[T], error) {\n\treturn compiledFetch.PrepareContext(context.Background(), db)\n}\n\n// PrepareContext is like Prepare but additionally requires a context.Context.\nfunc (compiledFetch *CompiledFetch[T]) PrepareContext(ctx context.Context, db DB) (*PreparedFetch[T], error) {\n\tvar err error\n\tpreparedFetch := &PreparedFetch[T]{\n\t\tcompiledFetch: NewCompiledFetch(compiledFetch.GetSQL()),\n\t}\n\tpreparedFetch.compiledFetch.queryIsStatic = compiledFetch.queryIsStatic\n\tif db == nil {\n\t\treturn nil, fmt.Errorf(\"db is nil\")\n\t}\n\tpreparedFetch.stmt, err = db.PrepareContext(ctx, compiledFetch.query)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpreparedFetch.logger, _ = db.(SqLogger)\n\tif preparedFetch.logger == nil {\n\t\tlogQuery, _ := defaultLogQuery.Load().(func(context.Context, QueryStats))\n\t\tif logQuery != nil {\n\t\t\tlogSettings, _ := defaultLogSettings.Load().(func(context.Context, *LogSettings))\n\t\t\tpreparedFetch.logger = &sqLogStruct{\n\t\t\t\tlogSettings: logSettings,\n\t\t\t\tlogQuery:    logQuery,\n\t\t\t}\n\t\t}\n\t}\n\treturn preparedFetch, nil\n}\n\n// PreparedFetch is the result of preparing a CompiledFetch on a DB.\ntype PreparedFetch[T any] struct {\n\tcompiledFetch *CompiledFetch[T]\n\tstmt          *sql.Stmt\n\tlogger        SqLogger\n}\n\n// PrepareFetch returns a new PreparedFetch.\nfunc PrepareFetch[T any](db DB, q Query, rowmapper func(*Row) T) (*PreparedFetch[T], error) {\n\treturn PrepareFetchContext(context.Background(), db, q, rowmapper)\n}\n\n// PrepareFetchContext is like PrepareFetch but additionally requires a context.Context.\nfunc PrepareFetchContext[T any](ctx context.Context, db DB, q Query, rowmapper func(*Row) T) (*PreparedFetch[T], error) {\n\tcompiledFetch, err := CompileFetchContext(ctx, q, rowmapper)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn compiledFetch.PrepareContext(ctx, db)\n}\n\n// FetchCursor returns a new cursor.\nfunc (preparedFetch PreparedFetch[T]) FetchCursor(params Params) (*Cursor[T], error) {\n\treturn preparedFetch.fetchCursor(context.Background(), params, 1)\n}\n\n// FetchCursorContext is like FetchCursor but additionally requires a context.Context.\nfunc (preparedFetch PreparedFetch[T]) FetchCursorContext(ctx context.Context, params Params) (*Cursor[T], error) {\n\treturn preparedFetch.fetchCursor(ctx, params, 1)\n}\n\nfunc (preparedFetch *PreparedFetch[T]) fetchCursor(ctx context.Context, params Params, skip int) (cursor *Cursor[T], err error) {\n\tcursor = &Cursor[T]{\n\t\tctx:       ctx,\n\t\trowmapper: preparedFetch.compiledFetch.rowmapper,\n\t\trow: &Row{\n\t\t\tdialect:       preparedFetch.compiledFetch.dialect,\n\t\t\tqueryIsStatic: preparedFetch.compiledFetch.queryIsStatic,\n\t\t},\n\t\tqueryStats: QueryStats{\n\t\t\tDialect:  preparedFetch.compiledFetch.dialect,\n\t\t\tQuery:    preparedFetch.compiledFetch.query,\n\t\t\tArgs:     preparedFetch.compiledFetch.args,\n\t\t\tParams:   preparedFetch.compiledFetch.params,\n\t\t\tRowCount: sql.NullInt64{Valid: true},\n\t\t},\n\t\tlogger: preparedFetch.logger,\n\t}\n\n\t// If the query is dynamic, call the rowmapper to populate row.scanDest.\n\tif !cursor.row.queryIsStatic {\n\t\tdefer mapperFunctionPanicked(&err)\n\t\t_ = cursor.rowmapper(cursor.row)\n\t}\n\n\t// Substitute params.\n\tcursor.queryStats.Args, err = substituteParams(cursor.queryStats.Dialect, cursor.queryStats.Args, cursor.queryStats.Params, params)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Setup logger.\n\tif cursor.logger != nil {\n\t\tcursor.logger.SqLogSettings(ctx, &cursor.logSettings)\n\t\tif cursor.logSettings.IncludeCaller {\n\t\t\tcursor.queryStats.CallerFile, cursor.queryStats.CallerLine, cursor.queryStats.CallerFunction = caller(skip + 1)\n\t\t}\n\t}\n\n\t// Run query.\n\tif cursor.logSettings.IncludeTime {\n\t\tcursor.queryStats.StartedAt = time.Now()\n\t}\n\tcursor.row.sqlRows, cursor.queryStats.Err = preparedFetch.stmt.QueryContext(ctx, cursor.queryStats.Args...)\n\tif cursor.logSettings.IncludeTime {\n\t\tcursor.queryStats.TimeTaken = time.Since(cursor.queryStats.StartedAt)\n\t}\n\tif cursor.queryStats.Err != nil {\n\t\treturn nil, cursor.queryStats.Err\n\t}\n\n\t// If the query is static, we now know the number of columns returned by\n\t// the query and can allocate the values slice and scanDest slice for\n\t// scanning later.\n\tif cursor.row.queryIsStatic {\n\t\tcursor.row.columns, err = cursor.row.sqlRows.Columns()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcursor.row.columnTypes, err = cursor.row.sqlRows.ColumnTypes()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcursor.row.columnIndex = make(map[string]int)\n\t\tfor index, column := range cursor.row.columns {\n\t\t\tcursor.row.columnIndex[column] = index\n\t\t}\n\t\tcursor.row.values = make([]any, len(cursor.row.columns))\n\t\tcursor.row.scanDest = make([]any, len(cursor.row.columns))\n\t\tfor index := range cursor.row.values {\n\t\t\tcursor.row.scanDest[index] = &cursor.row.values[index]\n\t\t}\n\t}\n\n\t// Allocate the resultsBuffer.\n\tif cursor.logSettings.IncludeResults > 0 {\n\t\tcursor.resultsBuffer = bufpool.Get().(*bytes.Buffer)\n\t\tcursor.resultsBuffer.Reset()\n\t}\n\treturn cursor, nil\n}\n\n// FetchOne returns the first result from running the PreparedFetch with the\n// give params.\nfunc (preparedFetch *PreparedFetch[T]) FetchOne(params Params) (T, error) {\n\tcursor, err := preparedFetch.fetchCursor(context.Background(), params, 1)\n\tif err != nil {\n\t\treturn *new(T), err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResult(cursor)\n}\n\n// FetchOneContext is like FetchOne but additionally requires a context.Context.\nfunc (preparedFetch *PreparedFetch[T]) FetchOneContext(ctx context.Context, params Params) (T, error) {\n\tcursor, err := preparedFetch.fetchCursor(ctx, params, 1)\n\tif err != nil {\n\t\treturn *new(T), err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResult(cursor)\n}\n\n// FetchAll returns all the results from running the PreparedFetch with the\n// give params.\nfunc (preparedFetch *PreparedFetch[T]) FetchAll(params Params) ([]T, error) {\n\tcursor, err := preparedFetch.fetchCursor(context.Background(), params, 1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResults(cursor)\n}\n\n// FetchAllContext is like FetchAll but additionally requires a context.Context.\nfunc (preparedFetch *PreparedFetch[T]) FetchAllContext(ctx context.Context, params Params) ([]T, error) {\n\tcursor, err := preparedFetch.fetchCursor(ctx, params, 1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cursor.Close()\n\treturn cursorResults(cursor)\n}\n\n// GetCompiled returns a copy of the underlying CompiledFetch.\nfunc (preparedFetch *PreparedFetch[T]) GetCompiled() *CompiledFetch[T] {\n\tcompiledFetch := NewCompiledFetch(preparedFetch.compiledFetch.GetSQL())\n\tcompiledFetch.queryIsStatic = preparedFetch.compiledFetch.queryIsStatic\n\treturn compiledFetch\n}\n\n// Close closes the PreparedFetch.\nfunc (preparedFetch *PreparedFetch[T]) Close() error {\n\tif preparedFetch.stmt == nil {\n\t\treturn nil\n\t}\n\treturn preparedFetch.stmt.Close()\n}\n\n// Exec executes the given Query on the given DB.\nfunc Exec(db DB, query Query) (Result, error) {\n\treturn exec(context.Background(), db, query, 1)\n}\n\n// ExecContext is like Exec but additionally requires a context.Context.\nfunc ExecContext(ctx context.Context, db DB, query Query) (Result, error) {\n\treturn exec(ctx, db, query, 1)\n}\n\nfunc exec(ctx context.Context, db DB, query Query, skip int) (result Result, err error) {\n\tif db == nil {\n\t\treturn result, fmt.Errorf(\"db is nil\")\n\t}\n\tif query == nil {\n\t\treturn result, fmt.Errorf(\"query is nil\")\n\t}\n\tdialect := query.GetDialect()\n\tif dialect == \"\" {\n\t\tdefaultDialect := DefaultDialect.Load()\n\t\tif defaultDialect != nil {\n\t\t\tdialect = *defaultDialect\n\t\t}\n\t}\n\tqueryStats := QueryStats{\n\t\tDialect: dialect,\n\t\tParams:  make(map[string][]int),\n\t}\n\n\t// Build query.\n\tbuf := bufpool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer bufpool.Put(buf)\n\terr = query.WriteSQL(ctx, dialect, buf, &queryStats.Args, queryStats.Params)\n\tqueryStats.Query = buf.String()\n\tif err != nil {\n\t\treturn result, err\n\t}\n\n\t// Setup logger.\n\tvar logSettings LogSettings\n\tlogger, _ := db.(SqLogger)\n\tif logger == nil {\n\t\tlogQuery, _ := defaultLogQuery.Load().(func(context.Context, QueryStats))\n\t\tif logQuery != nil {\n\t\t\tlogSettings, _ := defaultLogSettings.Load().(func(context.Context, *LogSettings))\n\t\t\tlogger = &sqLogStruct{\n\t\t\t\tlogSettings: logSettings,\n\t\t\t\tlogQuery:    logQuery,\n\t\t\t}\n\t\t}\n\t}\n\tif logger != nil {\n\t\tlogger.SqLogSettings(ctx, &logSettings)\n\t\tif logSettings.IncludeCaller {\n\t\t\tqueryStats.CallerFile, queryStats.CallerLine, queryStats.CallerFunction = caller(skip + 1)\n\t\t}\n\t\tdefer func() {\n\t\t\tif logSettings.LogAsynchronously {\n\t\t\t\tgo logger.SqLogQuery(ctx, queryStats)\n\t\t\t} else {\n\t\t\t\tlogger.SqLogQuery(ctx, queryStats)\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Run query.\n\tif logSettings.IncludeTime {\n\t\tqueryStats.StartedAt = time.Now()\n\t}\n\tvar sqlResult sql.Result\n\tsqlResult, queryStats.Err = db.ExecContext(ctx, queryStats.Query, queryStats.Args...)\n\tif logSettings.IncludeTime {\n\t\tqueryStats.TimeTaken = time.Since(queryStats.StartedAt)\n\t}\n\tif queryStats.Err != nil {\n\t\treturn result, queryStats.Err\n\t}\n\treturn execResult(sqlResult, &queryStats)\n}\n\n// CompiledExec is the result of compiling a Query down into a query string and\n// args slice. A CompiledExec can be safely executed in parallel.\ntype CompiledExec struct {\n\tdialect string\n\tquery   string\n\targs    []any\n\tparams  map[string][]int\n}\n\n// NewCompiledExec returns a new CompiledExec.\nfunc NewCompiledExec(dialect string, query string, args []any, params map[string][]int) *CompiledExec {\n\treturn &CompiledExec{\n\t\tdialect: dialect,\n\t\tquery:   query,\n\t\targs:    args,\n\t\tparams:  params,\n\t}\n}\n\n// CompileExec returns a new CompiledExec.\nfunc CompileExec(query Query) (*CompiledExec, error) {\n\treturn CompileExecContext(context.Background(), query)\n}\n\n// CompileExecContext is like CompileExec but additionally requires a context.Context.\nfunc CompileExecContext(ctx context.Context, query Query) (*CompiledExec, error) {\n\tif query == nil {\n\t\treturn nil, fmt.Errorf(\"query is nil\")\n\t}\n\tdialect := query.GetDialect()\n\tif dialect == \"\" {\n\t\tdefaultDialect := DefaultDialect.Load()\n\t\tif defaultDialect != nil {\n\t\t\tdialect = *defaultDialect\n\t\t}\n\t}\n\tcompiledExec := &CompiledExec{\n\t\tdialect: dialect,\n\t\tparams:  make(map[string][]int),\n\t}\n\n\t// Build query.\n\tbuf := bufpool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer bufpool.Put(buf)\n\terr := query.WriteSQL(ctx, dialect, buf, &compiledExec.args, compiledExec.params)\n\tcompiledExec.query = buf.String()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn compiledExec, nil\n}\n\n// Exec executes the CompiledExec on the given DB with the given params.\nfunc (compiledExec *CompiledExec) Exec(db DB, params Params) (Result, error) {\n\treturn compiledExec.exec(context.Background(), db, params, 1)\n}\n\n// ExecContext is like Exec but additionally requires a context.Context.\nfunc (compiledExec *CompiledExec) ExecContext(ctx context.Context, db DB, params Params) (Result, error) {\n\treturn compiledExec.exec(ctx, db, params, 1)\n}\n\nfunc (compiledExec *CompiledExec) exec(ctx context.Context, db DB, params Params, skip int) (result Result, err error) {\n\tif db == nil {\n\t\treturn result, fmt.Errorf(\"db is nil\")\n\t}\n\tqueryStats := QueryStats{\n\t\tDialect: compiledExec.dialect,\n\t\tQuery:   compiledExec.query,\n\t\tArgs:    compiledExec.args,\n\t\tParams:  compiledExec.params,\n\t}\n\n\t// Setup logger.\n\tvar logSettings LogSettings\n\tlogger, _ := db.(SqLogger)\n\tif logger == nil {\n\t\tlogQuery, _ := defaultLogQuery.Load().(func(context.Context, QueryStats))\n\t\tif logQuery != nil {\n\t\t\tlogSettings, _ := defaultLogSettings.Load().(func(context.Context, *LogSettings))\n\t\t\tlogger = &sqLogStruct{\n\t\t\t\tlogSettings: logSettings,\n\t\t\t\tlogQuery:    logQuery,\n\t\t\t}\n\t\t}\n\t}\n\tif logger != nil {\n\t\tlogger.SqLogSettings(ctx, &logSettings)\n\t\tif logSettings.IncludeCaller {\n\t\t\tqueryStats.CallerFile, queryStats.CallerLine, queryStats.CallerFunction = caller(skip + 1)\n\t\t}\n\t\tdefer func() {\n\t\t\tif logSettings.LogAsynchronously {\n\t\t\t\tgo logger.SqLogQuery(ctx, queryStats)\n\t\t\t} else {\n\t\t\t\tlogger.SqLogQuery(ctx, queryStats)\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Substitute params.\n\tqueryStats.Args, err = substituteParams(queryStats.Dialect, queryStats.Args, queryStats.Params, params)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\n\t// Run query.\n\tif logSettings.IncludeTime {\n\t\tqueryStats.StartedAt = time.Now()\n\t}\n\tvar sqlResult sql.Result\n\tsqlResult, queryStats.Err = db.ExecContext(ctx, queryStats.Query, queryStats.Args...)\n\tif logSettings.IncludeTime {\n\t\tqueryStats.TimeTaken = time.Since(queryStats.StartedAt)\n\t}\n\tif queryStats.Err != nil {\n\t\treturn result, queryStats.Err\n\t}\n\treturn execResult(sqlResult, &queryStats)\n}\n\n// GetSQL returns a copy of the dialect, query, args, params and rowmapper that\n// make up the CompiledExec.\nfunc (compiledExec *CompiledExec) GetSQL() (dialect string, query string, args []any, params map[string][]int) {\n\tdialect = compiledExec.dialect\n\tquery = compiledExec.query\n\targs = make([]any, len(compiledExec.args))\n\tparams = make(map[string][]int)\n\tcopy(args, compiledExec.args)\n\tfor name, indexes := range compiledExec.params {\n\t\tindexes2 := make([]int, len(indexes))\n\t\tcopy(indexes2, indexes)\n\t\tparams[name] = indexes2\n\t}\n\treturn dialect, query, args, params\n}\n\n// Prepare creates a PreparedExec from a CompiledExec by preparing it on the\n// given DB.\nfunc (compiledExec *CompiledExec) Prepare(db DB) (*PreparedExec, error) {\n\treturn compiledExec.PrepareContext(context.Background(), db)\n}\n\n// PrepareContext is like Prepare but additionally requires a context.Context.\nfunc (compiledExec *CompiledExec) PrepareContext(ctx context.Context, db DB) (*PreparedExec, error) {\n\tvar err error\n\tpreparedExec := &PreparedExec{\n\t\tcompiledExec: NewCompiledExec(compiledExec.GetSQL()),\n\t}\n\tpreparedExec.stmt, err = db.PrepareContext(ctx, compiledExec.query)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpreparedExec.logger, _ = db.(SqLogger)\n\tif preparedExec.logger == nil {\n\t\tlogQuery, _ := defaultLogQuery.Load().(func(context.Context, QueryStats))\n\t\tif logQuery != nil {\n\t\t\tlogSettings, _ := defaultLogSettings.Load().(func(context.Context, *LogSettings))\n\t\t\tpreparedExec.logger = &sqLogStruct{\n\t\t\t\tlogSettings: logSettings,\n\t\t\t\tlogQuery:    logQuery,\n\t\t\t}\n\t\t}\n\t}\n\treturn preparedExec, nil\n}\n\n// PrepareExec is the result of preparing a CompiledExec on a DB.\ntype PreparedExec struct {\n\tcompiledExec *CompiledExec\n\tstmt         *sql.Stmt\n\tlogger       SqLogger\n}\n\n// PrepareExec returns a new PreparedExec.\nfunc PrepareExec(db DB, q Query) (*PreparedExec, error) {\n\treturn PrepareExecContext(context.Background(), db, q)\n}\n\n// PrepareExecContext is like PrepareExec but additionally requires a\n// context.Context.\nfunc PrepareExecContext(ctx context.Context, db DB, q Query) (*PreparedExec, error) {\n\tcompiledExec, err := CompileExecContext(ctx, q)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn compiledExec.PrepareContext(ctx, db)\n}\n\n// Close closes the PreparedExec.\nfunc (preparedExec *PreparedExec) Close() error {\n\tif preparedExec.stmt == nil {\n\t\treturn nil\n\t}\n\treturn preparedExec.stmt.Close()\n}\n\n// Exec executes the PreparedExec with the given params.\nfunc (preparedExec *PreparedExec) Exec(params Params) (Result, error) {\n\treturn preparedExec.exec(context.Background(), params, 1)\n}\n\n// ExecContext is like Exec but additionally requires a context.Context.\nfunc (preparedExec *PreparedExec) ExecContext(ctx context.Context, params Params) (Result, error) {\n\treturn preparedExec.exec(ctx, params, 1)\n}\n\nfunc (preparedExec *PreparedExec) exec(ctx context.Context, params Params, skip int) (result Result, err error) {\n\tqueryStats := QueryStats{\n\t\tDialect: preparedExec.compiledExec.dialect,\n\t\tQuery:   preparedExec.compiledExec.query,\n\t\tArgs:    preparedExec.compiledExec.args,\n\t\tParams:  preparedExec.compiledExec.params,\n\t}\n\n\t// Setup logger.\n\tvar logSettings LogSettings\n\tif preparedExec.logger != nil {\n\t\tpreparedExec.logger.SqLogSettings(ctx, &logSettings)\n\t\tif logSettings.IncludeCaller {\n\t\t\tqueryStats.CallerFile, queryStats.CallerLine, queryStats.CallerFunction = caller(skip + 1)\n\t\t}\n\t\tdefer func() {\n\t\t\tif logSettings.LogAsynchronously {\n\t\t\t\tgo preparedExec.logger.SqLogQuery(ctx, queryStats)\n\t\t\t} else {\n\t\t\t\tpreparedExec.logger.SqLogQuery(ctx, queryStats)\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Substitute params.\n\tqueryStats.Args, err = substituteParams(queryStats.Dialect, queryStats.Args, queryStats.Params, params)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\n\t// Run query.\n\tif logSettings.IncludeTime {\n\t\tqueryStats.StartedAt = time.Now()\n\t}\n\tvar sqlResult sql.Result\n\tsqlResult, queryStats.Err = preparedExec.stmt.ExecContext(ctx, queryStats.Args...)\n\tif logSettings.IncludeTime {\n\t\tqueryStats.TimeTaken = time.Since(queryStats.StartedAt)\n\t}\n\tif queryStats.Err != nil {\n\t\treturn result, queryStats.Err\n\t}\n\treturn execResult(sqlResult, &queryStats)\n}\n\nfunc getFieldNames(ctx context.Context, row *Row) []string {\n\tif len(row.fields) == 0 {\n\t\tcolumns, _ := row.sqlRows.Columns()\n\t\treturn columns\n\t}\n\tbuf := bufpool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer bufpool.Put(buf)\n\tvar args []any\n\tfieldNames := make([]string, 0, len(row.fields))\n\tfor _, field := range row.fields {\n\t\tif alias := getAlias(field); alias != \"\" {\n\t\t\tfieldNames = append(fieldNames, alias)\n\t\t\tcontinue\n\t\t}\n\t\tbuf.Reset()\n\t\targs = args[:0]\n\t\terr := field.WriteSQL(ctx, row.dialect, buf, &args, nil)\n\t\tif err != nil {\n\t\t\tfieldNames = append(fieldNames, \"%!(error=\"+err.Error()+\")\")\n\t\t\tcontinue\n\t\t}\n\t\tfieldName, err := Sprintf(row.dialect, buf.String(), args)\n\t\tif err != nil {\n\t\t\tfieldNames = append(fieldNames, \"%!(error=\"+err.Error()+\")\")\n\t\t\tcontinue\n\t\t}\n\t\tfieldNames = append(fieldNames, fieldName)\n\t}\n\treturn fieldNames\n}\n\nfunc getFieldMappings(dialect string, fields []Field, scanDest []any) string {\n\tvar buf bytes.Buffer\n\tvar args []any\n\tvar b strings.Builder\n\tfor i, field := range fields {\n\t\tb.WriteString(fmt.Sprintf(\"\\n %02d. \", i+1))\n\t\tbuf.Reset()\n\t\targs = args[:0]\n\t\terr := field.WriteSQL(context.Background(), dialect, &buf, &args, nil)\n\t\tif err != nil {\n\t\t\tbuf.WriteString(\"%!(error=\" + err.Error() + \")\")\n\t\t\tcontinue\n\t\t}\n\t\tfieldName, err := Sprintf(dialect, buf.String(), args)\n\t\tif err != nil {\n\t\t\tb.WriteString(\"%!(error=\" + err.Error() + \")\")\n\t\t\tcontinue\n\t\t}\n\t\tb.WriteString(fieldName + \" => \" + reflect.TypeOf(scanDest[i]).String())\n\t}\n\treturn b.String()\n}\n\n// TODO: inline cursorResult, cursorResults and execResult.\n\nfunc cursorResult[T any](cursor *Cursor[T]) (result T, err error) {\n\tfor cursor.Next() {\n\t\tresult, err = cursor.Result()\n\t\tif err != nil {\n\t\t\treturn result, err\n\t\t}\n\t\tbreak\n\t}\n\tif cursor.RowCount() == 0 {\n\t\treturn result, sql.ErrNoRows\n\t}\n\treturn result, cursor.Close()\n}\n\nfunc cursorResults[T any](cursor *Cursor[T]) (results []T, err error) {\n\tvar result T\n\tfor cursor.Next() {\n\t\tresult, err = cursor.Result()\n\t\tif err != nil {\n\t\t\treturn results, err\n\t\t}\n\t\tresults = append(results, result)\n\t}\n\treturn results, cursor.Close()\n}\n\nfunc execResult(sqlResult sql.Result, queryStats *QueryStats) (Result, error) {\n\tvar err error\n\tvar result Result\n\tif queryStats.Dialect == DialectSQLite || queryStats.Dialect == DialectMySQL {\n\t\tresult.LastInsertId, err = sqlResult.LastInsertId()\n\t\tif err != nil {\n\t\t\treturn result, err\n\t\t}\n\t\tqueryStats.LastInsertId.Valid = true\n\t\tqueryStats.LastInsertId.Int64 = result.LastInsertId\n\t}\n\tresult.RowsAffected, err = sqlResult.RowsAffected()\n\tif err != nil {\n\t\treturn result, err\n\t}\n\tqueryStats.RowsAffected.Valid = true\n\tqueryStats.RowsAffected.Int64 = result.RowsAffected\n\treturn result, nil\n}\n\n// FetchExists returns a boolean indicating if running the given Query on the\n// given DB returned any results.\nfunc FetchExists(db DB, query Query) (exists bool, err error) {\n\treturn fetchExists(context.Background(), db, query, 1)\n}\n\n// FetchExistsContext is like FetchExists but additionally requires a\n// context.Context.\nfunc FetchExistsContext(ctx context.Context, db DB, query Query) (exists bool, err error) {\n\treturn fetchExists(ctx, db, query, 1)\n}\n\nfunc fetchExists(ctx context.Context, db DB, query Query, skip int) (exists bool, err error) {\n\tdialect := query.GetDialect()\n\tif dialect == \"\" {\n\t\tdefaultDialect := DefaultDialect.Load()\n\t\tif defaultDialect != nil {\n\t\t\tdialect = *defaultDialect\n\t\t}\n\t}\n\tqueryStats := QueryStats{\n\t\tDialect: dialect,\n\t\tParams:  make(map[string][]int),\n\t\tExists:  sql.NullBool{Valid: true},\n\t}\n\n\t// Build query.\n\tbuf := bufpool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer bufpool.Put(buf)\n\tif dialect == DialectSQLServer {\n\t\tquery = Queryf(\"SELECT CASE WHEN EXISTS ({}) THEN 1 ELSE 0 END\", query)\n\t} else {\n\t\tquery = Queryf(\"SELECT EXISTS ({})\", query)\n\t}\n\terr = query.WriteSQL(ctx, dialect, buf, &queryStats.Args, queryStats.Params)\n\tqueryStats.Query = buf.String()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\t// Setup logger.\n\tvar logSettings LogSettings\n\tlogger, _ := db.(SqLogger)\n\tif logger == nil {\n\t\tlogQuery, _ := defaultLogQuery.Load().(func(context.Context, QueryStats))\n\t\tif logQuery != nil {\n\t\t\tlogSettings, _ := defaultLogSettings.Load().(func(context.Context, *LogSettings))\n\t\t\tlogger = &sqLogStruct{\n\t\t\t\tlogSettings: logSettings,\n\t\t\t\tlogQuery:    logQuery,\n\t\t\t}\n\t\t}\n\t}\n\tif logger != nil {\n\t\tlogger.SqLogSettings(ctx, &logSettings)\n\t\tif logSettings.IncludeCaller {\n\t\t\tqueryStats.CallerFile, queryStats.CallerLine, queryStats.CallerFunction = caller(skip + 1)\n\t\t}\n\t\tdefer func() {\n\t\t\tif logSettings.LogAsynchronously {\n\t\t\t\tgo logger.SqLogQuery(ctx, queryStats)\n\t\t\t} else {\n\t\t\t\tlogger.SqLogQuery(ctx, queryStats)\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Run query.\n\tif logSettings.IncludeTime {\n\t\tqueryStats.StartedAt = time.Now()\n\t}\n\tvar sqlRows *sql.Rows\n\tsqlRows, queryStats.Err = db.QueryContext(ctx, queryStats.Query, queryStats.Args...)\n\tif logSettings.IncludeTime {\n\t\tqueryStats.TimeTaken = time.Since(queryStats.StartedAt)\n\t}\n\tif queryStats.Err != nil {\n\t\treturn false, queryStats.Err\n\t}\n\n\tfor sqlRows.Next() {\n\t\terr = sqlRows.Scan(&exists)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\tbreak\n\t}\n\tqueryStats.Exists.Bool = exists\n\n\tif err := sqlRows.Close(); err != nil {\n\t\treturn exists, err\n\t}\n\tif err := sqlRows.Err(); err != nil {\n\t\treturn exists, err\n\t}\n\treturn exists, nil\n}\n\n// substituteParams will return a new args slice by substituting values from\n// the given paramValues. The input args slice is untouched.\nfunc substituteParams(dialect string, args []any, paramIndexes map[string][]int, paramValues map[string]any) ([]any, error) {\n\tif len(paramValues) == 0 {\n\t\treturn args, nil\n\t}\n\tnewArgs := make([]any, len(args))\n\tcopy(newArgs, args)\n\tvar err error\n\tfor name, value := range paramValues {\n\t\tindexes := paramIndexes[name]\n\t\tfor _, index := range indexes {\n\t\t\tswitch arg := newArgs[index].(type) {\n\t\t\tcase sql.NamedArg:\n\t\t\t\targ.Value, err = preprocessValue(dialect, value)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tnewArgs[index] = arg\n\t\t\tdefault:\n\t\t\t\tvalue, err = preprocessValue(dialect, value)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tnewArgs[index] = value\n\t\t\t}\n\t\t}\n\t}\n\treturn newArgs, nil\n}\n\nfunc caller(skip int) (file string, line int, function string) {\n\tpc, file, line, _ := runtime.Caller(skip + 1)\n\tfn := runtime.FuncForPC(pc)\n\tfunction = fn.Name()\n\treturn file, line, function\n}\n"
  },
  {
    "path": "fetch_exec_test.go",
    "content": "package sq\n\nimport (\n\t\"database/sql\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\nvar ACTOR = New[struct {\n\tTableStruct `sq:\"actor\"`\n\tACTOR_ID    NumberField\n\tFIRST_NAME  StringField\n\tLAST_NAME   StringField\n\tLAST_UPDATE TimeField\n}](\"\")\n\ntype Actor struct {\n\tActorID    int\n\tFirstName  string\n\tLastName   string\n\tLastUpdate time.Time\n}\n\nfunc actorRowMapper(row *Row) Actor {\n\tvar actor Actor\n\tactorID, _ := row.Value(\"actor.actor_id\").(int64)\n\tactor.ActorID = int(actorID)\n\tactor.FirstName = row.StringField(ACTOR.FIRST_NAME)\n\tactor.LastName = row.StringField(ACTOR.LAST_NAME)\n\tactor.LastUpdate, _ = row.Value(\"actor.last_update\").(time.Time)\n\treturn actor\n}\n\nfunc actorRowMapperRawSQL(row *Row) Actor {\n\tresult := make(map[string]any)\n\tvalues := row.Values()\n\tfor i, column := range row.Columns() {\n\t\tresult[column] = values[i]\n\t}\n\tvar actor Actor\n\tactorID, _ := result[\"actor_id\"].(int64)\n\tactor.ActorID = int(actorID)\n\tactor.FirstName, _ = result[\"first_name\"].(string)\n\tactor.LastName, _ = result[\"last_name\"].(string)\n\tactor.LastUpdate, _ = result[\"last_update\"].(time.Time)\n\treturn actor\n}\n\nfunc Test_substituteParams(t *testing.T) {\n\tt.Run(\"no params provided\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\targs := []any{1, 2, 3}\n\t\tparams := map[string][]int{\"one\": {0}, \"two\": {1}, \"three\": {2}}\n\t\tgotArgs, err := substituteParams(\"\", args, params, nil)\n\t\tif err != nil {\n\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t}\n\t\twantArgs := []any{1, 2, 3}\n\t\tif diff := testutil.Diff(gotArgs, wantArgs); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"not all params provided\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\targs := []any{1, 2, 3}\n\t\tparams := map[string][]int{\"one\": {0}, \"two\": {1}, \"three\": {2}}\n\t\tparamValues := Params{\"one\": \"One\", \"two\": \"Two\"}\n\t\tgotArgs, err := substituteParams(\"\", args, params, paramValues)\n\t\tif err != nil {\n\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t}\n\t\twantArgs := []any{\"One\", \"Two\", 3}\n\t\tif diff := testutil.Diff(gotArgs, wantArgs); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"params substituted\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\ttype Data struct {\n\t\t\tid   int\n\t\t\tname string\n\t\t}\n\t\targs := []any{\n\t\t\t0,\n\t\t\tsql.Named(\"one\", 1),\n\t\t\tsql.Named(\"two\", 2),\n\t\t\t3,\n\t\t}\n\t\tparams := map[string][]int{\n\t\t\t\"zero\":  {0},\n\t\t\t\"one\":   {1},\n\t\t\t\"two\":   {2},\n\t\t\t\"three\": {3},\n\t\t}\n\t\tparamValues := Params{\n\t\t\t\"one\":   \"[one]\",\n\t\t\t\"two\":   \"[two]\",\n\t\t\t\"three\": \"[three]\",\n\t\t}\n\t\twantArgs := []any{\n\t\t\t0,\n\t\t\tsql.Named(\"one\", \"[one]\"),\n\t\t\tsql.Named(\"two\", \"[two]\"),\n\t\t\t\"[three]\",\n\t\t}\n\t\tgotArgs, err := substituteParams(\"\", args, params, paramValues)\n\t\tif err != nil {\n\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t}\n\t\tif diff := testutil.Diff(gotArgs, wantArgs); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n}\n\nfunc Test_getFieldMappings(t *testing.T) {\n\ttype TestTable struct {\n\t\tdescription       string\n\t\tdialect           string\n\t\tfields            []Field\n\t\tscanDest          []any\n\t\twantFieldMappings string\n\t}\n\n\tvar tests = []TestTable{{\n\t\tdescription:       \"empty\",\n\t\twantFieldMappings: \"\",\n\t}, {\n\t\tdescription: \"basic\",\n\t\tfields: []Field{\n\t\t\tExpr(\"actor_id\"),\n\t\t\tExpr(\"first_name || {} || last_name\", \" \"),\n\t\t\tExpr(\"last_update\"),\n\t\t},\n\t\tscanDest: []any{\n\t\t\t&sql.NullInt64{},\n\t\t\t&sql.NullString{},\n\t\t\t&sql.NullTime{},\n\t\t},\n\t\twantFieldMappings: \"\" +\n\t\t\t\"\\n 01. actor_id => *sql.NullInt64\" +\n\t\t\t\"\\n 02. first_name || ' ' || last_name => *sql.NullString\" +\n\t\t\t\"\\n 03. last_update => *sql.NullTime\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tgotFieldMappings := getFieldMappings(tt.dialect, tt.fields, tt.scanDest)\n\t\t\tif diff := testutil.Diff(gotFieldMappings, tt.wantFieldMappings); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFetchExec(t *testing.T) {\n\tt.Parallel()\n\tdb := newDB(t)\n\n\tvar referenceActors = []Actor{\n\t\t{ActorID: 1, FirstName: \"PENELOPE\", LastName: \"GUINESS\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 2, FirstName: \"NICK\", LastName: \"WAHLBERG\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 3, FirstName: \"ED\", LastName: \"CHASE\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 4, FirstName: \"JENNIFER\", LastName: \"DAVIS\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 5, FirstName: \"JOHNNY\", LastName: \"LOLLOBRIGIDA\", LastUpdate: time.Unix(1, 0).UTC()},\n\t}\n\n\t// Exec.\n\tres, err := Exec(Log(db), SQLite.\n\t\tInsertInto(ACTOR).\n\t\tColumnValues(func(col *Column) {\n\t\t\tfor _, actor := range referenceActors {\n\t\t\t\tcol.SetInt(ACTOR.ACTOR_ID, actor.ActorID)\n\t\t\t\tcol.SetString(ACTOR.FIRST_NAME, actor.FirstName)\n\t\t\t\tcol.SetString(ACTOR.LAST_NAME, actor.LastName)\n\t\t\t\tcol.SetTime(ACTOR.LAST_UPDATE, actor.LastUpdate)\n\t\t\t}\n\t\t}),\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(res.RowsAffected, int64(len(referenceActors))); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), diff)\n\t}\n\n\t// FetchOne.\n\tactor, err := FetchOne(Log(db), SQLite.\n\t\tFrom(ACTOR).\n\t\tWhere(ACTOR.ACTOR_ID.EqInt(1)),\n\t\tactorRowMapper,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actor, referenceActors[0]); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), diff)\n\t}\n\n\t// FetchOne (Raw SQL).\n\tactor, err = FetchOne(Log(db),\n\t\tSQLite.Queryf(\"SELECT * FROM actor WHERE actor_id = {}\", 1),\n\t\tactorRowMapperRawSQL,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actor, referenceActors[0]); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), diff)\n\t}\n\n\t// FetchAll.\n\tactors, err := FetchAll(VerboseLog(db), SQLite.\n\t\tFrom(ACTOR).\n\t\tOrderBy(ACTOR.ACTOR_ID),\n\t\tactorRowMapper,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actors, referenceActors); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\n\t// FetchAll (RawSQL).\n\tactors, err = FetchAll(VerboseLog(db),\n\t\tSQLite.Queryf(\"SELECT * FROM actor ORDER BY actor_id\"),\n\t\tactorRowMapperRawSQL,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actors, referenceActors); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n}\n\nfunc TestCompiledFetchExec(t *testing.T) {\n\tt.Parallel()\n\tdb := newDB(t)\n\tvar referenceActors = []Actor{\n\t\t{ActorID: 1, FirstName: \"PENELOPE\", LastName: \"GUINESS\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 2, FirstName: \"NICK\", LastName: \"WAHLBERG\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 3, FirstName: \"ED\", LastName: \"CHASE\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 4, FirstName: \"JENNIFER\", LastName: \"DAVIS\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 5, FirstName: \"JOHNNY\", LastName: \"LOLLOBRIGIDA\", LastUpdate: time.Unix(1, 0).UTC()},\n\t}\n\n\t// CompiledExec.\n\tcompiledExec, err := CompileExec(SQLite.\n\t\tInsertInto(ACTOR).\n\t\tColumnValues(func(col *Column) {\n\t\t\tcol.Set(ACTOR.ACTOR_ID, IntParam(\"actor_id\", 0))\n\t\t\tcol.Set(ACTOR.FIRST_NAME, StringParam(\"first_name\", \"\"))\n\t\t\tcol.Set(ACTOR.LAST_NAME, StringParam(\"last_name\", \"\"))\n\t\t\tcol.Set(ACTOR.LAST_UPDATE, TimeParam(\"last_update\", time.Time{}))\n\t\t}),\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tfor _, actor := range referenceActors {\n\t\t_, err = compiledExec.Exec(Log(db), Params{\n\t\t\t\"actor_id\":    actor.ActorID,\n\t\t\t\"first_name\":  actor.FirstName,\n\t\t\t\"last_name\":   actor.LastName,\n\t\t\t\"last_update\": actor.LastUpdate,\n\t\t})\n\t\tif err != nil {\n\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t}\n\t}\n\n\t// CompiledFetch FetchOne.\n\tcompiledFetch, err := CompileFetch(SQLite.\n\t\tFrom(ACTOR).\n\t\tWhere(ACTOR.ACTOR_ID.Eq(IntParam(\"actor_id\", 0))),\n\t\tactorRowMapper,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tactor, err := compiledFetch.FetchOne(Log(db), Params{\"actor_id\": 1})\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actor, referenceActors[0]); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), diff)\n\t}\n\n\t// CompiledFetch FetchOne (Raw SQL).\n\tcompiledFetch, err = CompileFetch(\n\t\tSQLite.Queryf(\"SELECT * FROM actor WHERE actor_id = {actor_id}\", IntParam(\"actor_id\", 0)),\n\t\tactorRowMapperRawSQL,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tactor, err = compiledFetch.FetchOne(Log(db), Params{\"actor_id\": 1})\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actor, referenceActors[0]); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), diff)\n\t}\n\n\t// CompiledFetch FetchAll.\n\tcompiledFetch, err = CompileFetch(SQLite.\n\t\tFrom(ACTOR).\n\t\tOrderBy(ACTOR.ACTOR_ID),\n\t\tactorRowMapper,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tactors, err := compiledFetch.FetchAll(VerboseLog(db), nil)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actors, referenceActors); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), diff)\n\t}\n\n\t// CompiledFetch FetchAll (Raw SQL).\n\tcompiledFetch, err = CompileFetch(\n\t\tSQLite.Queryf(\"SELECT * FROM actor ORDER BY actor_id\"),\n\t\tactorRowMapperRawSQL,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tactors, err = compiledFetch.FetchAll(VerboseLog(db), nil)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actors, referenceActors); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), diff)\n\t}\n}\n\nfunc TestPreparedFetchExec(t *testing.T) {\n\tt.Parallel()\n\tdb := newDB(t)\n\n\tvar referenceActors = []Actor{\n\t\t{ActorID: 1, FirstName: \"PENELOPE\", LastName: \"GUINESS\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 2, FirstName: \"NICK\", LastName: \"WAHLBERG\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 3, FirstName: \"ED\", LastName: \"CHASE\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 4, FirstName: \"JENNIFER\", LastName: \"DAVIS\", LastUpdate: time.Unix(1, 0).UTC()},\n\t\t{ActorID: 5, FirstName: \"JOHNNY\", LastName: \"LOLLOBRIGIDA\", LastUpdate: time.Unix(1, 0).UTC()},\n\t}\n\n\t// PreparedExec.\n\tpreparedExec, err := PrepareExec(Log(db), SQLite.\n\t\tInsertInto(ACTOR).\n\t\tColumnValues(func(col *Column) {\n\t\t\tcol.Set(ACTOR.ACTOR_ID, IntParam(\"actor_id\", 0))\n\t\t\tcol.Set(ACTOR.FIRST_NAME, StringParam(\"first_name\", \"\"))\n\t\t\tcol.Set(ACTOR.LAST_NAME, StringParam(\"last_name\", \"\"))\n\t\t\tcol.Set(ACTOR.LAST_UPDATE, TimeParam(\"last_update\", time.Time{}))\n\t\t}),\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tfor _, actor := range referenceActors {\n\t\t_, err = preparedExec.Exec(Params{\n\t\t\t\"actor_id\":    actor.ActorID,\n\t\t\t\"first_name\":  actor.FirstName,\n\t\t\t\"last_name\":   actor.LastName,\n\t\t\t\"last_update\": actor.LastUpdate,\n\t\t})\n\t\tif err != nil {\n\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t}\n\t}\n\n\t// PreparedFetch FetchOne.\n\tpreparedFetch, err := PrepareFetch(Log(db), SQLite.\n\t\tFrom(ACTOR).\n\t\tWhere(ACTOR.ACTOR_ID.Eq(IntParam(\"actor_id\", 0))),\n\t\tactorRowMapper,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tactor, err := preparedFetch.FetchOne(Params{\"actor_id\": 1})\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actor, referenceActors[0]); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), diff)\n\t}\n\n\t// PreparedFetch FetchOne (Raw SQL).\n\tpreparedFetch, err = PrepareFetch(\n\t\tLog(db),\n\t\tSQLite.Queryf(\"SELECT * FROM actor WHERE actor_id = {actor_id}\", IntParam(\"actor_id\", 0)),\n\t\tactorRowMapperRawSQL,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tactor, err = preparedFetch.FetchOne(Params{\"actor_id\": 1})\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actor, referenceActors[0]); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), diff)\n\t}\n\n\t// PreparedFetch FetchAll.\n\tpreparedFetch, err = PrepareFetch(VerboseLog(db), SQLite.\n\t\tFrom(ACTOR).\n\t\tOrderBy(ACTOR.ACTOR_ID),\n\t\tactorRowMapper,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tactors, err := preparedFetch.FetchAll(nil)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actors, referenceActors); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), diff)\n\t}\n\n\t// PreparedFetch FetchAll (Raw SQL).\n\tpreparedFetch, err = PrepareFetch(\n\t\tVerboseLog(db),\n\t\tSQLite.Queryf(\"SELECT * FROM actor ORDER BY actor_id\"),\n\t\tactorRowMapperRawSQL,\n\t)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tactors, err = preparedFetch.FetchAll(nil)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(actors, referenceActors); diff != \"\" {\n\t\tt.Fatal(testutil.Callers(), diff)\n\t}\n}\n\nfunc newDB(t *testing.T) *sql.DB {\n\tdb, err := sql.Open(\"sqlite3\", \":memory:\")\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\t_, err = db.Exec(`CREATE TABLE actor (\n    actor_id INTEGER PRIMARY KEY AUTOINCREMENT\n    ,first_name TEXT NOT NULL\n    ,last_name TEXT NOT NULL\n    ,last_update DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP\n)`)\n\tif err != nil {\n\t\tt.Fatal(testutil.Callers(), err)\n\t}\n\treturn db\n}\n"
  },
  {
    "path": "fields.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Identifier represents an SQL identifier. If necessary, it will be quoted\n// according to the dialect.\ntype Identifier string\n\nvar _ Field = (*Identifier)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (id Identifier) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tbuf.WriteString(QuoteIdentifier(dialect, string(id)))\n\treturn nil\n}\n\n// IsField implements the Field interface.\nfunc (id Identifier) IsField() {}\n\n// AnyField is a catch-all field type that satisfies the Any interface.\ntype AnyField struct {\n\ttable      TableStruct\n\tname       string\n\talias      string\n\tdesc       sql.NullBool\n\tnullsfirst sql.NullBool\n}\n\nvar _ interface {\n\tField\n\tAny\n\tWithPrefix(string) Field\n} = (*AnyField)(nil)\n\n// NewAnyField returns a new AnyField.\nfunc NewAnyField(name string, tbl TableStruct) AnyField {\n\treturn AnyField{table: tbl, name: name}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (field AnyField) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\twriteFieldIdentifier(ctx, dialect, buf, args, params, field.table, field.name)\n\twriteFieldOrder(ctx, dialect, buf, args, params, field.desc, field.nullsfirst)\n\treturn nil\n}\n\n// As returns a new AnyField with the given alias.\nfunc (field AnyField) As(alias string) AnyField {\n\tfield.alias = alias\n\treturn field\n}\n\n// Asc returns a new AnyField indicating that it should be ordered in ascending\n// order i.e. 'ORDER BY field ASC'.\nfunc (field AnyField) Asc() AnyField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = false\n\treturn field\n}\n\n// Desc returns a new AnyField indicating that it should be ordered in descending\n// order i.e. 'ORDER BY field DESC'.\nfunc (field AnyField) Desc() AnyField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = true\n\treturn field\n}\n\n// NullsLast returns a new NumberField indicating that it should be ordered\n// with nulls last i.e. 'ORDER BY field NULLS LAST'.\nfunc (field AnyField) NullsLast() AnyField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = false\n\treturn field\n}\n\n// NullsFirst returns a new NumberField indicating that it should be ordered\n// with nulls first i.e. 'ORDER BY field NULLS FIRST'.\nfunc (field AnyField) NullsFirst() AnyField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = true\n\treturn field\n}\n\n// WithPrefix returns a new Field that with the given prefix.\nfunc (field AnyField) WithPrefix(prefix string) Field {\n\tfield.table.alias = \"\"\n\tfield.table.name = prefix\n\treturn field\n}\n\n// IsNull returns a 'field IS NULL' Predicate.\nfunc (field AnyField) IsNull() Predicate { return Expr(\"{} IS NULL\", field) }\n\n// IsNotNull returns a 'field IS NOT NULL' Predicate.\nfunc (field AnyField) IsNotNull() Predicate { return Expr(\"{} IS NOT NULL\", field) }\n\n// In returns a 'field IN (value)' Predicate. The value can be a slice, which\n// corresponds to the expression 'field IN (x, y, z)'.\nfunc (field AnyField) In(value any) Predicate { return In(field, value) }\n\n// In returns a 'field NOT IN (value)' Predicate. The value can be a slice, which\n// corresponds to the expression 'field NOT IN (x, y, z)'.\nfunc (field AnyField) NotIn(value any) Predicate { return NotIn(field, value) }\n\n// Eq returns a 'field = value' Predicate.\nfunc (field AnyField) Eq(value any) Predicate { return Eq(field, value) }\n\n// Ne returns a 'field <> value' Predicate.\nfunc (field AnyField) Ne(value any) Predicate { return Ne(field, value) }\n\n// Lt returns a 'field < value' Predicate.\nfunc (field AnyField) Lt(value any) Predicate { return Lt(field, value) }\n\n// Le returns a 'field <= value' Predicate.\nfunc (field AnyField) Le(value any) Predicate { return Le(field, value) }\n\n// Gt returns a 'field > value' Predicate.\nfunc (field AnyField) Gt(value any) Predicate { return Gt(field, value) }\n\n// Ge returns a 'field >= value' Predicate.\nfunc (field AnyField) Ge(value any) Predicate { return Ge(field, value) }\n\n// Expr returns an expression where the field is prepended to the front of the\n// expression.\nfunc (field AnyField) Expr(format string, values ...any) Expression {\n\tvalues = append(values, field)\n\tordinal := len(values)\n\treturn Expr(\"{\"+strconv.Itoa(ordinal)+\"} \"+format, values...)\n}\n\n// Set returns an Assignment assigning the value to the field.\nfunc (field AnyField) Set(value any) Assignment {\n\treturn Set(field, value)\n}\n\n// Setf returns an Assignment assigning an expression to the field.\nfunc (field AnyField) Setf(format string, values ...any) Assignment {\n\treturn Setf(field, format, values...)\n}\n\n// GetAlias returns the alias of the AnyField.\nfunc (field AnyField) GetAlias() string { return field.alias }\n\n// IsField implements the Field interface.\nfunc (field AnyField) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (field AnyField) IsArray() {}\n\n// IsBinary implements the Binary interface.\nfunc (field AnyField) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (field AnyField) IsBoolean() {}\n\n// IsEnum implements the Enum interface.\nfunc (field AnyField) IsEnum() {}\n\n// IsJSON implements the JSONValue interface.\nfunc (field AnyField) IsJSON() {}\n\n// IsNumber implements the Number interface.\nfunc (field AnyField) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (field AnyField) IsString() {}\n\n// IsTime implements the Time interface.\nfunc (field AnyField) IsTime() {}\n\n// IsUUIDType implements the UUID interface.\nfunc (field AnyField) IsUUID() {}\n\n// ArrayField represents an SQL array field.\ntype ArrayField struct {\n\ttable TableStruct\n\tname  string\n\talias string\n}\n\nvar _ interface {\n\tField\n\tArray\n\tWithPrefix(string) Field\n} = (*ArrayField)(nil)\n\n// NewArrayField returns a new ArrayField.\nfunc NewArrayField(fieldName string, tableName TableStruct) ArrayField {\n\treturn ArrayField{table: tableName, name: fieldName}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (field ArrayField) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\twriteFieldIdentifier(ctx, dialect, buf, args, params, field.table, field.name)\n\treturn nil\n}\n\n// As returns a new ArrayField with the given alias.\nfunc (field ArrayField) As(alias string) ArrayField {\n\tfield.alias = alias\n\treturn field\n}\n\n// WithPrefix returns a new Field that with the given prefix.\nfunc (field ArrayField) WithPrefix(prefix string) Field {\n\tfield.table.alias = \"\"\n\tfield.table.name = prefix\n\treturn field\n}\n\n// IsNull returns a 'field IS NULL' Predicate.\nfunc (field ArrayField) IsNull() Predicate { return Expr(\"{} IS NULL\", field) }\n\n// IsNull returns a 'field IS NOT NULL' Predicate.\nfunc (field ArrayField) IsNotNull() Predicate { return Expr(\"{} IS NOT NULL\", field) }\n\n// Set returns an Assignment assigning the value to the field.\nfunc (field ArrayField) Set(value any) Assignment {\n\tswitch value.(type) {\n\tcase SQLWriter:\n\t\treturn Set(field, value)\n\tcase []string, []int, []int64, []int32, []float64, []float32, []bool:\n\t\treturn Set(field, ArrayValue(value))\n\t}\n\treturn Set(field, value)\n}\n\n// SetArray returns an Assignment assigning the value to the field. It wraps\n// the value with ArrayValue().\nfunc (field ArrayField) SetArray(value any) Assignment {\n\treturn Set(field, ArrayValue(value))\n}\n\n// Setf returns an Assignment assigning an expression to the field.\nfunc (field ArrayField) Setf(format string, values ...any) Assignment {\n\treturn Set(field, Expr(format, values...))\n}\n\n// GetAlias returns the alias of the ArrayField.\nfunc (field ArrayField) GetAlias() string { return field.alias }\n\n// IsField implements the Field interface.\nfunc (field ArrayField) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (field ArrayField) IsArray() {}\n\n// BinaryField represents an SQL binary field.\ntype BinaryField struct {\n\ttable      TableStruct\n\tname       string\n\talias      string\n\tdesc       sql.NullBool\n\tnullsfirst sql.NullBool\n}\n\nvar _ interface {\n\tField\n\tBinary\n\tWithPrefix(string) Field\n} = (*BinaryField)(nil)\n\n// NewBinaryField returns a new BinaryField.\nfunc NewBinaryField(fieldName string, tableName TableStruct) BinaryField {\n\treturn BinaryField{table: tableName, name: fieldName}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (field BinaryField) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\twriteFieldIdentifier(ctx, dialect, buf, args, params, field.table, field.name)\n\twriteFieldOrder(ctx, dialect, buf, args, params, field.desc, field.nullsfirst)\n\treturn nil\n}\n\n// As returns a new BinaryField with the given alias.\nfunc (field BinaryField) As(alias string) BinaryField {\n\tfield.alias = alias\n\treturn field\n}\n\n// Asc returns a new BinaryField indicating that it should be ordered in ascending\n// order i.e. 'ORDER BY field ASC'.\nfunc (field BinaryField) Asc() BinaryField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = false\n\treturn field\n}\n\n// Desc returns a new BinaryField indicating that it should be ordered in ascending\n// order i.e. 'ORDER BY field DESC'.\nfunc (field BinaryField) Desc() BinaryField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = true\n\treturn field\n}\n\n// NullsLast returns a new BinaryField indicating that it should be ordered\n// with nulls last i.e. 'ORDER BY field NULLS LAST'.\nfunc (field BinaryField) NullsLast() BinaryField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = false\n\treturn field\n}\n\n// NullsFirst returns a new BinaryField indicating that it should be ordered\n// with nulls first i.e. 'ORDER BY field NULLS FIRST'.\nfunc (field BinaryField) NullsFirst() BinaryField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = true\n\treturn field\n}\n\n// WithPrefix returns a new Field that with the given prefix.\nfunc (field BinaryField) WithPrefix(prefix string) Field {\n\tfield.table.alias = \"\"\n\tfield.table.name = prefix\n\treturn field\n}\n\n// IsNull returns a 'field IS NULL' Predicate.\nfunc (field BinaryField) IsNull() Predicate { return Expr(\"{} IS NULL\", field) }\n\n// IsNotNull returns a 'field IS NOT NULL' Predicate.\nfunc (field BinaryField) IsNotNull() Predicate { return Expr(\"{} IS NOT NULL\", field) }\n\n// Eq returns a 'field = value' Predicate.\nfunc (field BinaryField) Eq(value Binary) Predicate { return Eq(field, value) }\n\n// Ne returns a 'field <> value' Predicate.\nfunc (field BinaryField) Ne(value Binary) Predicate { return Ne(field, value) }\n\n// EqBytes returns a 'field = b' Predicate.\nfunc (field BinaryField) EqBytes(b []byte) Predicate { return Eq(field, b) }\n\n// NeBytes returns a 'field <> b' Predicate.\nfunc (field BinaryField) NeBytes(b []byte) Predicate { return Ne(field, b) }\n\n// Set returns an Assignment assigning the value to the field.\nfunc (field BinaryField) Set(value any) Assignment {\n\treturn Set(field, value)\n}\n\n// Setf returns an Assignment assigning an expression to the field.\nfunc (field BinaryField) Setf(format string, values ...any) Assignment {\n\treturn Setf(field, format, values...)\n}\n\n// SetBytes returns an Assignment assigning a []byte to the field.\nfunc (field BinaryField) SetBytes(b []byte) Assignment { return Set(field, b) }\n\n// GetAlias returns the alias of the BinaryField.\nfunc (field BinaryField) GetAlias() string { return field.alias }\n\n// IsField implements the Field interface.\nfunc (field BinaryField) IsField() {}\n\n// IsBinary implements the Binary interface.\nfunc (field BinaryField) IsBinary() {}\n\n// BooleanField represents an SQL boolean field.\ntype BooleanField struct {\n\ttable      TableStruct\n\tname       string\n\talias      string\n\tdesc       sql.NullBool\n\tnullsfirst sql.NullBool\n}\n\nvar _ interface {\n\tField\n\tBoolean\n\tPredicate\n\tWithPrefix(string) Field\n} = (*BooleanField)(nil)\n\n// NewBooleanField returns a new BooleanField.\nfunc NewBooleanField(fieldName string, tableName TableStruct) BooleanField {\n\treturn BooleanField{table: tableName, name: fieldName}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (field BooleanField) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\twriteFieldIdentifier(ctx, dialect, buf, args, params, field.table, field.name)\n\twriteFieldOrder(ctx, dialect, buf, args, params, field.desc, field.nullsfirst)\n\treturn nil\n}\n\n// As returns a new BooleanField with the given alias.\nfunc (field BooleanField) As(alias string) BooleanField {\n\tfield.alias = alias\n\treturn field\n}\n\n// Asc returns a new BooleanField indicating that it should be ordered in ascending\n// order i.e. 'ORDER BY field ASC'.\nfunc (field BooleanField) Asc() BooleanField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = false\n\treturn field\n}\n\n// Desc returns a new BooleanField indicating that it should be ordered in\n// descending order i.e. 'ORDER BY field DESC'.\nfunc (f BooleanField) Desc() BooleanField {\n\tf.desc.Valid = true\n\tf.desc.Bool = true\n\treturn f\n}\n\n// NullsLast returns a new BooleanField indicating that it should be ordered\n// with nulls last i.e. 'ORDER BY field NULLS LAST'.\nfunc (field BooleanField) NullsLast() BooleanField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = false\n\treturn field\n}\n\n// NullsFirst returns a new BooleanField indicating that it should be ordered\n// with nulls first i.e. 'ORDER BY field NULLS FIRST'.\nfunc (field BooleanField) NullsFirst() BooleanField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = true\n\treturn field\n}\n\n// WithPrefix returns a new Field that with the given prefix.\nfunc (field BooleanField) WithPrefix(prefix string) Field {\n\tfield.table.alias = \"\"\n\tfield.table.name = prefix\n\treturn field\n}\n\n// IsNull returns a 'field IS NULL' Predicate.\nfunc (field BooleanField) IsNull() Predicate { return Expr(\"{} IS NULL\", field) }\n\n// IsNotNull returns a 'field IS NOT NULL' Predicate.\nfunc (field BooleanField) IsNotNull() Predicate { return Expr(\"{} IS NOT NULL\", field) }\n\n// Eq returns a 'field = value' Predicate.\nfunc (field BooleanField) Eq(value Boolean) Predicate { return Eq(field, value) }\n\n// Ne returns a 'field <> value' Predicate.\nfunc (field BooleanField) Ne(value Boolean) Predicate { return Ne(field, value) }\n\n// EqBool returns a 'field = b' Predicate.\nfunc (field BooleanField) EqBool(b bool) Predicate { return Eq(field, b) }\n\n// NeBool returns a 'field <> b' Predicate.\nfunc (field BooleanField) NeBool(b bool) Predicate { return Ne(field, b) }\n\n// Set returns an Assignment assigning the value to the field.\nfunc (field BooleanField) Set(value any) Assignment {\n\treturn Set(field, value)\n}\n\n// Setf returns an Assignment assigning an expression to the field.\nfunc (field BooleanField) Setf(format string, values ...any) Assignment {\n\treturn Setf(field, format, values...)\n}\n\n// SetBool returns an Assignment assigning a bool to the field i.e. 'field =\n// b'.\nfunc (field BooleanField) SetBool(b bool) Assignment { return Set(field, b) }\n\n// GetAlias returns the alias of the BooleanField.\nfunc (field BooleanField) GetAlias() string { return field.alias }\n\n// IsField implements the Field interface.\nfunc (field BooleanField) IsField() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (field BooleanField) IsBoolean() {}\n\n// EnumField represents an SQL enum field.\ntype EnumField struct {\n\ttable TableStruct\n\tname  string\n\talias string\n}\n\nvar _ interface {\n\tField\n\tEnum\n\tWithPrefix(string) Field\n} = (*EnumField)(nil)\n\n// NewEnumField returns a new EnumField.\nfunc NewEnumField(name string, tbl TableStruct) EnumField {\n\treturn EnumField{table: tbl, name: name}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (field EnumField) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\twriteFieldIdentifier(ctx, dialect, buf, args, params, field.table, field.name)\n\treturn nil\n}\n\n// As returns a new EnumField with the given alias.\nfunc (field EnumField) As(alias string) EnumField {\n\tfield.alias = alias\n\treturn field\n}\n\n// WithPrefix returns a new Field that with the given prefix.\nfunc (field EnumField) WithPrefix(prefix string) Field {\n\tfield.table.alias = \"\"\n\tfield.table.name = prefix\n\treturn field\n}\n\n// IsNull returns a 'field IS NULL' Predicate.\nfunc (field EnumField) IsNull() Predicate { return Expr(\"{} IS NULL\", field) }\n\n// IsNotNull returns a 'field IS NOT NULL' Predicate.\nfunc (field EnumField) IsNotNull() Predicate { return Expr(\"{} IS NOT NULL\", field) }\n\n// In returns a 'field IN (value)' Predicate. The value can be a slice, which\n// corresponds to the expression 'field IN (x, y, z)'.\nfunc (field EnumField) In(value any) Predicate { return In(field, value) }\n\n// NotIn returns a 'field NOT IN (value)' Predicate. The value can be a slice, which\n// corresponds to the expression 'field NOT IN (x, y, z)'.\nfunc (field EnumField) NotIn(value any) Predicate { return NotIn(field, value) }\n\n// Eq returns a 'field = value' Predicate.\nfunc (field EnumField) Eq(value any) Predicate { return Eq(field, value) }\n\n// Ne returns a 'field <> value' Predicate.\nfunc (field EnumField) Ne(value any) Predicate { return Ne(field, value) }\n\n// EqEnum returns a 'field = value' Predicate. It wraps the value with\n// EnumValue().\nfunc (field EnumField) EqEnum(value Enumeration) Predicate { return Eq(field, EnumValue(value)) }\n\n// NeEnum returns a 'field <> value' Predicate. it wraps the value with\n// EnumValue().\nfunc (field EnumField) NeEnum(value Enumeration) Predicate { return Ne(field, EnumValue(value)) }\n\n// Set returns an Assignment assigning the value to the field.\nfunc (field EnumField) Set(value any) Assignment {\n\treturn Set(field, value)\n}\n\n// SetEnum returns an Assignment assigning the value to the field. It wraps the\n// value with EnumValue().\nfunc (field EnumField) SetEnum(value Enumeration) Assignment {\n\treturn Set(field, EnumValue(value))\n}\n\n// Setf returns an Assignment assigning an expression to the field.\nfunc (field EnumField) Setf(format string, values ...any) Assignment {\n\treturn Setf(field, format, values...)\n}\n\n// GetAlias returns the alias of the EnumField.\nfunc (field EnumField) GetAlias() string { return field.alias }\n\n// IsField implements the Field interface.\nfunc (field EnumField) IsField() {}\n\n// IsEnum implements the Enum interface.\nfunc (field EnumField) IsEnum() {}\n\n// JSONField represents an SQL JSON field.\ntype JSONField struct {\n\ttable TableStruct\n\tname  string\n\talias string\n}\n\nvar _ interface {\n\tField\n\tBinary\n\tJSON\n\tString\n\tWithPrefix(string) Field\n} = (*JSONField)(nil)\n\n// NewJSONField returns a new JSONField.\nfunc NewJSONField(name string, tbl TableStruct) JSONField {\n\treturn JSONField{table: tbl, name: name}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (field JSONField) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\twriteFieldIdentifier(ctx, dialect, buf, args, params, field.table, field.name)\n\treturn nil\n}\n\n// As returns a new JSONField with the given alias.\nfunc (field JSONField) As(alias string) JSONField {\n\tfield.alias = alias\n\treturn field\n}\n\n// WithPrefix returns a new Field that with the given prefix.\nfunc (field JSONField) WithPrefix(prefix string) Field {\n\tfield.table.alias = \"\"\n\tfield.table.name = prefix\n\treturn field\n}\n\n// IsNull returns a 'field IS NULL' Predicate.\nfunc (field JSONField) IsNull() Predicate { return Expr(\"{} IS NULL\", field) }\n\n// IsNotNull returns a 'field IS NOT NULL' Predicate.\nfunc (field JSONField) IsNotNull() Predicate { return Expr(\"{} IS NOT NULL\", field) }\n\n// Set returns an Assignment assigning the value to the field.\nfunc (field JSONField) Set(value any) Assignment {\n\tswitch value.(type) {\n\tcase []byte, driver.Valuer, SQLWriter:\n\t\treturn Set(field, value)\n\t}\n\tswitch reflect.TypeOf(value).Kind() {\n\tcase reflect.Map, reflect.Struct, reflect.Slice, reflect.Array:\n\t\treturn Set(field, JSONValue(value))\n\t}\n\treturn Set(field, value)\n}\n\n// SetJSON returns an Assignment assigning the value to the field. It wraps the\n// value in JSONValue().\nfunc (field JSONField) SetJSON(value any) Assignment {\n\treturn Set(field, JSONValue(value))\n}\n\n// Setf returns an Assignment assigning an expression to the field.\nfunc (field JSONField) Setf(format string, values ...any) Assignment {\n\treturn Setf(field, format, values...)\n}\n\n// GetAlias returns the alias of the JSONField.\nfunc (field JSONField) GetAlias() string { return field.alias }\n\n// IsField implements the Field interface.\nfunc (field JSONField) IsField() {}\n\n// IsBinary implements the Binary interface.\nfunc (field JSONField) IsBinary() {}\n\n// IsJSON implements the JSON interface.\nfunc (field JSONField) IsJSON() {}\n\n// IsString implements the String interface.\nfunc (field JSONField) IsString() {}\n\n// NumberField represents an SQL number field.\ntype NumberField struct {\n\ttable      TableStruct\n\tname       string\n\talias      string\n\tdesc       sql.NullBool\n\tnullsfirst sql.NullBool\n}\n\nvar _ interface {\n\tField\n\tNumber\n\tWithPrefix(string) Field\n} = (*NumberField)(nil)\n\n// NewNumberField returns a new NumberField.\nfunc NewNumberField(name string, tbl TableStruct) NumberField {\n\treturn NumberField{table: tbl, name: name}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (field NumberField) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\twriteFieldIdentifier(ctx, dialect, buf, args, params, field.table, field.name)\n\twriteFieldOrder(ctx, dialect, buf, args, params, field.desc, field.nullsfirst)\n\treturn nil\n}\n\n// As returns a new NumberField with the given alias.\nfunc (field NumberField) As(alias string) NumberField {\n\tfield.alias = alias\n\treturn field\n}\n\n// Asc returns a new NumberField indicating that it should be ordered in ascending\n// order i.e. 'ORDER BY field ASC'.\nfunc (field NumberField) Asc() NumberField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = false\n\treturn field\n}\n\n// Desc returns a new NumberField indicating that it should be ordered in ascending\n// order i.e. 'ORDER BY field DESC'.\nfunc (field NumberField) Desc() NumberField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = true\n\treturn field\n}\n\n// NullsLast returns a new NumberField indicating that it should be ordered\n// with nulls last i.e. 'ORDER BY field NULLS LAST'.\nfunc (field NumberField) NullsLast() NumberField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = false\n\treturn field\n}\n\n// NullsFirst returns a new NumberField indicating that it should be ordered\n// with nulls first i.e. 'ORDER BY field NULLS FIRST'.\nfunc (field NumberField) NullsFirst() NumberField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = true\n\treturn field\n}\n\n// WithPrefix returns a new Field that with the given prefix.\nfunc (field NumberField) WithPrefix(prefix string) Field {\n\tfield.table.alias = \"\"\n\tfield.table.name = prefix\n\treturn field\n}\n\n// IsNull returns a 'field IS NULL' Predicate.\nfunc (field NumberField) IsNull() Predicate { return Expr(\"{} IS NULL\", field) }\n\n// IsNotNull returns a 'field IS NOT NULL' Predicate.\nfunc (field NumberField) IsNotNull() Predicate { return Expr(\"{} IS NOT NULL\", field) }\n\n// In returns a 'field IN (value)' Predicate. The value can be a slice, which\n// corresponds to the expression 'field IN (x, y, z)'.\nfunc (field NumberField) In(value any) Predicate { return In(field, value) }\n\n// NotIn returns a 'field NOT IN (value)' Predicate. The value can be a slice,\n// which corresponds to the expression 'field IN (x, y, z)'.\nfunc (field NumberField) NotIn(value any) Predicate { return NotIn(field, value) }\n\n// Eq returns a 'field = value' Predicate.\nfunc (field NumberField) Eq(value Number) Predicate { return Eq(field, value) }\n\n// Ne returns a 'field <> value' Predicate.\nfunc (field NumberField) Ne(value Number) Predicate { return Ne(field, value) }\n\n// Lt returns a 'field < value' Predicate.\nfunc (field NumberField) Lt(value Number) Predicate { return Lt(field, value) }\n\n// Le returns a 'field <= value' Predicate.\nfunc (field NumberField) Le(value Number) Predicate { return Le(field, value) }\n\n// Gt returns a 'field > value' Predicate.\nfunc (field NumberField) Gt(value Number) Predicate { return Gt(field, value) }\n\n// Ge returns a 'field >= value' Predicate.\nfunc (field NumberField) Ge(value Number) Predicate { return Ge(field, value) }\n\n// EqInt returns a 'field = num' Predicate.\nfunc (field NumberField) EqInt(num int) Predicate { return Eq(field, num) }\n\n// NeInt returns a 'field <> num' Predicate.\nfunc (field NumberField) NeInt(num int) Predicate { return Ne(field, num) }\n\n// LtInt returns a 'field < num' Predicate.\nfunc (field NumberField) LtInt(num int) Predicate { return Lt(field, num) }\n\n// LeInt returns a 'field <= num' Predicate.\nfunc (field NumberField) LeInt(num int) Predicate { return Le(field, num) }\n\n// GtInt returns a 'field > num' Predicate.\nfunc (field NumberField) GtInt(num int) Predicate { return Gt(field, num) }\n\n// GeInt returns a 'field >= num' Predicate.\nfunc (field NumberField) GeInt(num int) Predicate { return Ge(field, num) }\n\n// EqInt64 returns a 'field = num' Predicate.\nfunc (field NumberField) EqInt64(num int64) Predicate { return Eq(field, num) }\n\n// NeInt64 returns a 'field <> num' Predicate.\nfunc (field NumberField) NeInt64(num int64) Predicate { return Ne(field, num) }\n\n// LtInt64 returns a 'field < num' Predicate.\nfunc (field NumberField) LtInt64(num int64) Predicate { return Lt(field, num) }\n\n// LeInt64 returns a 'field <= num' Predicate.\nfunc (field NumberField) LeInt64(num int64) Predicate { return Le(field, num) }\n\n// GtInt64 returns a 'field > num' Predicate.\nfunc (field NumberField) GtInt64(num int64) Predicate { return Gt(field, num) }\n\n// GeInt64 returns a 'field >= num' Predicate.\nfunc (field NumberField) GeInt64(num int64) Predicate { return Ge(field, num) }\n\n// EqFloat64 returns a 'field = num' Predicate.\nfunc (field NumberField) EqFloat64(num float64) Predicate { return Eq(field, num) }\n\n// NeFloat64 returns a 'field <> num' Predicate.\nfunc (field NumberField) NeFloat64(num float64) Predicate { return Ne(field, num) }\n\n// LtFloat64 returns a 'field < num' Predicate.\nfunc (field NumberField) LtFloat64(num float64) Predicate { return Lt(field, num) }\n\n// LeFloat64 returns a 'field <= num' Predicate.\nfunc (field NumberField) LeFloat64(num float64) Predicate { return Le(field, num) }\n\n// GtFloat64 returns a 'field > num' Predicate.\nfunc (field NumberField) GtFloat64(num float64) Predicate { return Gt(field, num) }\n\n// GeFloat64 returns a 'field >= num' Predicate.\nfunc (field NumberField) GeFloat64(num float64) Predicate { return Ge(field, num) }\n\n// Set returns an Assignment assigning the value to the field.\nfunc (field NumberField) Set(value any) Assignment {\n\treturn Set(field, value)\n}\n\n// Setf returns an Assignment assigning an expression to the field.\nfunc (field NumberField) Setf(format string, values ...any) Assignment {\n\treturn Setf(field, format, values...)\n}\n\n// SetBytes returns an Assignment assigning an int to the field.\nfunc (field NumberField) SetInt(num int) Assignment { return Set(field, num) }\n\n// SetBytes returns an Assignment assigning an int64 to the field.\nfunc (field NumberField) SetInt64(num int64) Assignment { return Set(field, num) }\n\n// SetBytes returns an Assignment assigning an float64 to the field.\nfunc (field NumberField) SetFloat64(num float64) Assignment { return Set(field, num) }\n\n// GetAlias returns the alias of the NumberField.\nfunc (field NumberField) GetAlias() string { return field.alias }\n\n// IsField implements the Field interface.\nfunc (field NumberField) IsField() {}\n\n// IsNumber implements the Number interface.\nfunc (field NumberField) IsNumber() {}\n\n// StringField represents an SQL string field.\ntype StringField struct {\n\ttable      TableStruct\n\tname       string\n\talias      string\n\tcollation  string\n\tdesc       sql.NullBool\n\tnullsfirst sql.NullBool\n}\n\nvar _ interface {\n\tField\n\tString\n\tWithPrefix(string) Field\n} = (*StringField)(nil)\n\n// NewStringField returns a new StringField.\nfunc NewStringField(name string, tbl TableStruct) StringField {\n\treturn StringField{table: tbl, name: name}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (field StringField) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\twriteFieldIdentifier(ctx, dialect, buf, args, params, field.table, field.name)\n\tif field.collation != \"\" {\n\t\tbuf.WriteString(\" COLLATE \")\n\t\tif dialect == DialectPostgres {\n\t\t\tbuf.WriteString(`\"` + EscapeQuote(field.collation, '\"') + `\"`)\n\t\t} else {\n\t\t\tbuf.WriteString(QuoteIdentifier(dialect, field.collation))\n\t\t}\n\t}\n\twriteFieldOrder(ctx, dialect, buf, args, params, field.desc, field.nullsfirst)\n\treturn nil\n}\n\n// As returns a new StringField with the given alias.\nfunc (field StringField) As(alias string) StringField {\n\tfield.alias = alias\n\treturn field\n}\n\n// Collate returns a new StringField using the given collation.\nfunc (field StringField) Collate(collation string) StringField {\n\tfield.collation = collation\n\treturn field\n}\n\n// Asc returns a new StringField indicating that it should be ordered in\n// ascending order i.e. 'ORDER BY field ASC'.\nfunc (field StringField) Asc() StringField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = false\n\treturn field\n}\n\n// Desc returns a new StringField indicating that it should be ordered in\n// descending order i.e. 'ORDER BY field DESC'.\nfunc (field StringField) Desc() StringField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = true\n\treturn field\n}\n\n// NullsLast returns a new StringField indicating that it should be ordered\n// with nulls last i.e. 'ORDER BY field NULLS LAST'.\nfunc (field StringField) NullsLast() StringField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = false\n\treturn field\n}\n\n// NullsFirst returns a new StringField indicating that it should be ordered\n// with nulls first i.e. 'ORDER BY field NULLS FIRST'.\nfunc (field StringField) NullsFirst() StringField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = true\n\treturn field\n}\n\n// WithPrefix returns a new Field that with the given prefix.\nfunc (field StringField) WithPrefix(prefix string) Field {\n\tfield.table.alias = \"\"\n\tfield.table.name = prefix\n\treturn field\n}\n\n// IsNull returns a 'field IS NULL' Predicate.\nfunc (field StringField) IsNull() Predicate { return Expr(\"{} IS NULL\", field) }\n\n// IsNotNull returns a 'field IS NOT NULL' Predicate.\nfunc (field StringField) IsNotNull() Predicate { return Expr(\"{} IS NOT NULL\", field) }\n\n// In returns a 'field IN (value)' Predicate. The value can be a slice, which\n// corresponds to the expression 'field IN (x, y, z)'.\nfunc (field StringField) In(value any) Predicate { return In(field, value) }\n\n// In returns a 'field NOT IN (value)' Predicate. The value can be a slice,\n// which corresponds to the expression 'field NOT IN (x, y, z)'.\nfunc (field StringField) NotIn(value any) Predicate { return NotIn(field, value) }\n\n// Eq returns a 'field = value' Predicate.\nfunc (field StringField) Eq(value String) Predicate { return Eq(field, value) }\n\n// Ne returns a 'field <> value' Predicate.\nfunc (field StringField) Ne(value String) Predicate { return Ne(field, value) }\n\n// Lt returns a 'field < value' Predicate.\nfunc (field StringField) Lt(value String) Predicate { return Lt(field, value) }\n\n// Le returns a 'field <= value' Predicate.\nfunc (field StringField) Le(value String) Predicate { return Le(field, value) }\n\n// Gt returns a 'field > value' Predicate.\nfunc (field StringField) Gt(value String) Predicate { return Gt(field, value) }\n\n// Ge returns a 'field >= value' Predicate.\nfunc (field StringField) Ge(value String) Predicate { return Ge(field, value) }\n\n// EqString returns a 'field = str' Predicate.\nfunc (field StringField) EqString(str string) Predicate { return Eq(field, str) }\n\n// NeString returns a 'field <> str' Predicate.\nfunc (field StringField) NeString(str string) Predicate { return Ne(field, str) }\n\n// LtString returns a 'field < str' Predicate.\nfunc (field StringField) LtString(str string) Predicate { return Lt(field, str) }\n\n// LeString returns a 'field <= str' Predicate.\nfunc (field StringField) LeString(str string) Predicate { return Le(field, str) }\n\n// GtString returns a 'field > str' Predicate.\nfunc (field StringField) GtString(str string) Predicate { return Gt(field, str) }\n\n// GeString returns a 'field >= str' Predicate.\nfunc (field StringField) GeString(str string) Predicate { return Ge(field, str) }\n\n// LikeString returns a 'field LIKE str' Predicate.\nfunc (field StringField) LikeString(str string) Predicate {\n\treturn Expr(\"{} LIKE {}\", field, str)\n}\n\n// NotLikeString returns a 'field NOT LIKE str' Predicate.\nfunc (field StringField) NotLikeString(str string) Predicate {\n\treturn Expr(\"{} NOT LIKE {}\", field, str)\n}\n\n// ILikeString returns a 'field ILIKE str' Predicate.\nfunc (field StringField) ILikeString(str string) Predicate {\n\treturn Expr(\"{} ILIKE {}\", field, str)\n}\n\n// NotILikeString returns a 'field NOT ILIKE str' Predicate.\nfunc (field StringField) NotILikeString(str string) Predicate {\n\treturn Expr(\"{} NOT ILIKE {}\", field, str)\n}\n\n// Set returns an Assignment assigning the value to the field.\nfunc (field StringField) Set(value any) Assignment {\n\treturn Set(field, value)\n}\n\n// Setf returns an Assignment assigning an expression to the field.\nfunc (field StringField) Setf(format string, values ...any) Assignment {\n\treturn Setf(field, format, values...)\n}\n\n// SetString returns an Assignment assigning a string to the field.\nfunc (field StringField) SetString(str string) Assignment { return Set(field, str) }\n\n// GetAlias returns the alias of the StringField.\nfunc (field StringField) GetAlias() string { return field.alias }\n\n// IsField implements the Field interface.\nfunc (field StringField) IsField() {}\n\n// IsString implements the String interface.\nfunc (field StringField) IsString() {}\n\n// TimeField represents an SQL time field.\ntype TimeField struct {\n\ttable      TableStruct\n\tname       string\n\talias      string\n\tdesc       sql.NullBool\n\tnullsfirst sql.NullBool\n}\n\nvar _ interface {\n\tField\n\tTime\n\tWithPrefix(string) Field\n} = (*TimeField)(nil)\n\n// NewTimeField returns a new TimeField.\nfunc NewTimeField(name string, tbl TableStruct) TimeField {\n\treturn TimeField{table: tbl, name: name}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (field TimeField) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\twriteFieldIdentifier(ctx, dialect, buf, args, params, field.table, field.name)\n\twriteFieldOrder(ctx, dialect, buf, args, params, field.desc, field.nullsfirst)\n\treturn nil\n}\n\n// As returns a new TimeField with the given alias.\nfunc (field TimeField) As(alias string) TimeField {\n\tfield.alias = alias\n\treturn field\n}\n\n// Asc returns a new TimeField indicating that it should be ordered in ascending\n// order i.e. 'ORDER BY field ASC'.\nfunc (field TimeField) Asc() TimeField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = false\n\treturn field\n}\n\n// Desc returns a new TimeField indicating that it should be ordered in ascending\n// order i.e. 'ORDER BY field DESC'.\nfunc (field TimeField) Desc() TimeField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = true\n\treturn field\n}\n\n// NullsLast returns a new TimeField indicating that it should be ordered\n// with nulls last i.e. 'ORDER BY field NULLS LAST'.\nfunc (field TimeField) NullsLast() TimeField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = false\n\treturn field\n}\n\n// NullsFirst returns a new TimeField indicating that it should be ordered\n// with nulls first i.e. 'ORDER BY field NULLS FIRST'.\nfunc (field TimeField) NullsFirst() TimeField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = true\n\treturn field\n}\n\n// WithPrefix returns a new Field that with the given prefix.\nfunc (field TimeField) WithPrefix(prefix string) Field {\n\tfield.table.alias = \"\"\n\tfield.table.name = prefix\n\treturn field\n}\n\n// IsNull returns a 'field IS NULL' Predicate.\nfunc (field TimeField) IsNull() Predicate { return Expr(\"{} IS NULL\", field) }\n\n// IsNotNull returns a 'field IS NOT NULL' Predicate.\nfunc (field TimeField) IsNotNull() Predicate { return Expr(\"{} IS NOT NULL\", field) }\n\n// In returns a 'field IN (value)' Predicate. The value can be a slice, which\n// corresponds to the expression 'field IN (x, y, z)'.\nfunc (field TimeField) In(value any) Predicate { return In(field, value) }\n\n// NotIn returns a 'field NOT IN (value)' Predicate. The value can be a slice,\n// which corresponds to the expression 'field NOT IN (x, y, z)'.\nfunc (field TimeField) NotIn(value any) Predicate { return NotIn(field, value) }\n\n// Eq returns a 'field = value' Predicate.\nfunc (field TimeField) Eq(value Time) Predicate { return Eq(field, value) }\n\n// Ne returns a 'field <> value' Predicate.\nfunc (field TimeField) Ne(value Time) Predicate { return Ne(field, value) }\n\n// Lt returns a 'field < value' Predicate.\nfunc (field TimeField) Lt(value Time) Predicate { return Lt(field, value) }\n\n// Le returns a 'field <= value' Predicate.\nfunc (field TimeField) Le(value Time) Predicate { return Le(field, value) }\n\n// Gt returns a 'field > value' Predicate.\nfunc (field TimeField) Gt(value Time) Predicate { return Gt(field, value) }\n\n// Ge returns a 'field >= value' Predicate.\nfunc (field TimeField) Ge(value Time) Predicate { return Ge(field, value) }\n\n// EqTime returns a 'field = t' Predicate.\nfunc (field TimeField) EqTime(t time.Time) Predicate { return Eq(field, t) }\n\n// NeTime returns a 'field <> t' Predicate.\nfunc (field TimeField) NeTime(t time.Time) Predicate { return Ne(field, t) }\n\n// LtTime returns a 'field < t' Predicate.\nfunc (field TimeField) LtTime(t time.Time) Predicate { return Lt(field, t) }\n\n// LeTime returns a 'field <= t' Predicate.\nfunc (field TimeField) LeTime(t time.Time) Predicate { return Le(field, t) }\n\n// GtTime returns a 'field > t' Predicate.\nfunc (field TimeField) GtTime(t time.Time) Predicate { return Gt(field, t) }\n\n// GeTime returns a 'field >= t' Predicate.\nfunc (field TimeField) GeTime(t time.Time) Predicate { return Ge(field, t) }\n\n// Set returns an Assignment assigning the value to the field.\nfunc (field TimeField) Set(value any) Assignment {\n\treturn Set(field, value)\n}\n\n// Setf returns an Assignment assigning an expression to the field.\nfunc (field TimeField) Setf(format string, values ...any) Assignment {\n\treturn Setf(field, format, values...)\n}\n\n// SetTime returns an Assignment assigning a time.Time to the field.\nfunc (field TimeField) SetTime(t time.Time) Assignment { return Set(field, t) }\n\n// GetAlias returns the alias of the TimeField.\nfunc (field TimeField) GetAlias() string { return field.alias }\n\n// IsField implements the Field interface.\nfunc (field TimeField) IsField() {}\n\n// IsTime implements the Time interface.\nfunc (field TimeField) IsTime() {}\n\n// Timestamp is as a replacement for sql.NullTime but with the following\n// enhancements:\n//\n// 1. Timestamp.Value() returns an int64 unix timestamp if the dialect is\n// SQLite, otherwise it returns a time.Time (similar to sql.NullTime).\n//\n// 2. Timestamp.Scan() additionally supports scanning from int64 and text\n// (string/[]byte) values on top of what sql.NullTime already supports. The\n// following text timestamp formats are supported:\n//\n//\tvar timestampFormats = []string{\n//\t\t\"2006-01-02 15:04:05.999999999-07:00\",\n//\t\t\"2006-01-02T15:04:05.999999999-07:00\",\n//\t\t\"2006-01-02 15:04:05.999999999\",\n//\t\t\"2006-01-02T15:04:05.999999999\",\n//\t\t\"2006-01-02 15:04:05\",\n//\t\t\"2006-01-02T15:04:05\",\n//\t\t\"2006-01-02 15:04\",\n//\t\t\"2006-01-02T15:04\",\n//\t\t\"2006-01-02\",\n//\t}\ntype Timestamp struct {\n\ttime.Time\n\tValid   bool\n\tdialect string\n}\n\n// NewTimestamp creates a new Timestamp from a time.Time.\nfunc NewTimestamp(t time.Time) Timestamp {\n\treturn Timestamp{Time: t, Valid: true}\n}\n\n// copied from https://pkg.go.dev/github.com/mattn/go-sqlite3#pkg-variables\nvar timestampFormats = []string{\n\t\"2006-01-02 15:04:05.999999999-07:00\",\n\t\"2006-01-02T15:04:05.999999999-07:00\",\n\t\"2006-01-02 15:04:05.999999999\",\n\t\"2006-01-02T15:04:05.999999999\",\n\t\"2006-01-02 15:04:05\",\n\t\"2006-01-02T15:04:05\",\n\t\"2006-01-02 15:04\",\n\t\"2006-01-02T15:04\",\n\t\"2006-01-02\",\n}\n\n// Scan implements the sql.Scanner interface. It additionally supports scanning\n// from int64 and text (string/[]byte) values on top of what sql.NullTime\n// already supports. The following text timestamp formats are supported:\n//\n//\tvar timestampFormats = []string{\n//\t\t\"2006-01-02 15:04:05.999999999-07:00\",\n//\t\t\"2006-01-02T15:04:05.999999999-07:00\",\n//\t\t\"2006-01-02 15:04:05.999999999\",\n//\t\t\"2006-01-02T15:04:05.999999999\",\n//\t\t\"2006-01-02 15:04:05\",\n//\t\t\"2006-01-02T15:04:05\",\n//\t\t\"2006-01-02 15:04\",\n//\t\t\"2006-01-02T15:04\",\n//\t\t\"2006-01-02\",\n//\t}\nfunc (ts *Timestamp) Scan(value any) error {\n\tif value == nil {\n\t\tts.Time, ts.Valid = time.Time{}, false\n\t\treturn nil\n\t}\n\t// int64 and string handling copied from\n\t// https://github.com/mattn/go-sqlite3/issues/748#issuecomment-538643131\n\tswitch value := value.(type) {\n\tcase int64:\n\t\t// Assume a millisecond unix timestamp if it's 13 digits -- too\n\t\t// large to be a reasonable timestamp in seconds.\n\t\tif value > 1e12 || value < -1e12 {\n\t\t\tvalue *= int64(time.Millisecond) // convert ms to nsec\n\t\t\tts.Time = time.Unix(0, value)\n\t\t} else {\n\t\t\tts.Time = time.Unix(value, 0)\n\t\t}\n\t\tts.Valid = true\n\t\treturn nil\n\tcase string:\n\t\tif len(value) == 0 {\n\t\t\tts.Time, ts.Valid = time.Time{}, false\n\t\t\treturn nil\n\t\t}\n\t\tvar err error\n\t\tvar timeVal time.Time\n\t\tvalue = strings.TrimSuffix(value, \"Z\")\n\t\tfor _, format := range timestampFormats {\n\t\t\tif timeVal, err = time.ParseInLocation(format, value, time.UTC); err == nil {\n\t\t\t\tts.Time, ts.Valid = timeVal, true\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn fmt.Errorf(\"could not convert %q into time\", value)\n\tcase []byte:\n\t\tif len(value) == 0 {\n\t\t\tts.Time, ts.Valid = time.Time{}, false\n\t\t\treturn nil\n\t\t}\n\t\tvar err error\n\t\tvar timeVal time.Time\n\t\tvalue = bytes.TrimSuffix(value, []byte(\"Z\"))\n\t\tfor _, format := range timestampFormats {\n\t\t\tif timeVal, err = time.ParseInLocation(format, string(value), time.UTC); err == nil {\n\t\t\t\tts.Time, ts.Valid = timeVal, true\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn fmt.Errorf(\"could not convert %q into time\", value)\n\tdefault:\n\t\tvar nulltime sql.NullTime\n\t\terr := nulltime.Scan(value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tts.Time, ts.Valid = nulltime.Time, nulltime.Valid\n\t\treturn nil\n\t}\n}\n\n// Value implements the driver.Valuer interface. It returns an int64 unix\n// timestamp if the dialect is SQLite, otherwise it returns a time.Time\n// (similar to sql.NullTime).\nfunc (ts Timestamp) Value() (driver.Value, error) {\n\tif !ts.Valid {\n\t\treturn nil, nil\n\t}\n\tif ts.dialect == DialectSQLite {\n\t\treturn ts.Time.UTC().Unix(), nil\n\t}\n\treturn ts.Time, nil\n}\n\n// DialectValuer implements the DialectValuer interface.\nfunc (ts Timestamp) DialectValuer(dialect string) (driver.Valuer, error) {\n\tts.dialect = dialect\n\treturn ts, nil\n}\n\n// UUIDField represents an SQL UUID field.\ntype UUIDField struct {\n\ttable      TableStruct\n\tname       string\n\talias      string\n\tdesc       sql.NullBool\n\tnullsfirst sql.NullBool\n}\n\nvar _ interface {\n\tField\n\tUUID\n\tWithPrefix(string) Field\n} = (*UUIDField)(nil)\n\n// NewUUIDField returns a new UUIDField.\nfunc NewUUIDField(name string, tbl TableStruct) UUIDField {\n\treturn UUIDField{table: tbl, name: name}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (field UUIDField) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\twriteFieldIdentifier(ctx, dialect, buf, args, params, field.table, field.name)\n\twriteFieldOrder(ctx, dialect, buf, args, params, field.desc, field.nullsfirst)\n\treturn nil\n}\n\n// As returns a new UUIDField with the given alias.\nfunc (field UUIDField) As(alias string) UUIDField {\n\tfield.alias = alias\n\treturn field\n}\n\n// Asc returns a new UUIDField indicating that it should be ordered in ascending\n// order i.e. 'ORDER BY field ASC'.\nfunc (field UUIDField) Asc() UUIDField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = false\n\treturn field\n}\n\n// Desc returns a new UUIDField indicating that it should be ordered in ascending\n// order i.e. 'ORDER BY field DESC'.\nfunc (field UUIDField) Desc() UUIDField {\n\tfield.desc.Valid = true\n\tfield.desc.Bool = true\n\treturn field\n}\n\n// NullsLast returns a new UUIDField indicating that it should be ordered\n// with nulls last i.e. 'ORDER BY field NULLS LAST'.\nfunc (field UUIDField) NullsLast() UUIDField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = false\n\treturn field\n}\n\n// NullsFirst returns a new UUIDField indicating that it should be ordered\n// with nulls first i.e. 'ORDER BY field NULLS FIRST'.\nfunc (field UUIDField) NullsFirst() UUIDField {\n\tfield.nullsfirst.Valid = true\n\tfield.nullsfirst.Bool = true\n\treturn field\n}\n\n// WithPrefix returns a new Field that with the given prefix.\nfunc (field UUIDField) WithPrefix(prefix string) Field {\n\tfield.table.alias = \"\"\n\tfield.table.name = prefix\n\treturn field\n}\n\n// IsNull returns a 'field IS NULL' Predicate.\nfunc (field UUIDField) IsNull() Predicate { return Expr(\"{} IS NULL\", field) }\n\n// IsNotNull returns a 'field IS NOT NULL' Predicate.\nfunc (field UUIDField) IsNotNull() Predicate { return Expr(\"{} IS NOT NULL\", field) }\n\n// In returns a 'field IN (value)' Predicate. The value can be a slice, which\n// corresponds to the expression 'field IN (x, y, z)'.\nfunc (field UUIDField) In(value any) Predicate { return In(field, value) }\n\n// NotIn returns a 'field NOT IN (value)' Predicate. The value can be a slice,\n// which corresponds to the expression 'field NOT IN (x, y, z)'.\nfunc (field UUIDField) NotIn(value any) Predicate { return NotIn(field, value) }\n\n// Eq returns a 'field = value' Predicate.\nfunc (field UUIDField) Eq(value any) Predicate { return Eq(field, value) }\n\n// Ne returns a 'field <> value' Predicate.\nfunc (field UUIDField) Ne(value any) Predicate { return Ne(field, value) }\n\n// EqUUID returns a 'field = value' Predicate. The value is wrapped in\n// UUIDValue().\nfunc (field UUIDField) EqUUID(value any) Predicate { return Eq(field, UUIDValue(value)) }\n\n// NeUUID returns a 'field <> value' Predicate. The value is wrapped in\n// UUIDValue().\nfunc (field UUIDField) NeUUID(value any) Predicate { return Ne(field, UUIDValue(value)) }\n\n// Set returns an Assignment assigning the value to the field.\nfunc (field UUIDField) Set(value any) Assignment {\n\treturn Set(field, value)\n}\n\n// SetUUID returns an Assignment assigning the value to the field. It wraps the\n// value in UUIDValue().\nfunc (field UUIDField) SetUUID(value any) Assignment {\n\treturn Set(field, UUIDValue(value))\n}\n\n// Set returns an Assignment assigning the value to the field.\nfunc (field UUIDField) Setf(format string, values ...any) Assignment {\n\treturn Setf(field, format, values...)\n}\n\n// GetAlias returns the alias of the UUIDField.\nfunc (field UUIDField) GetAlias() string { return field.alias }\n\n// IsField implements the Field interface.\nfunc (field UUIDField) IsField() {}\n\n// IsUUID implements the UUID interface.\nfunc (field UUIDField) IsUUID() {}\n\n// New instantiates a new table struct with the given alias. Passing in an\n// empty string is equivalent to giving no alias to the table.\nfunc New[T Table](alias string) T {\n\tvar tbl T\n\tptrvalue := reflect.ValueOf(&tbl)\n\tvalue := reflect.Indirect(ptrvalue)\n\ttyp := value.Type()\n\tif typ.Kind() != reflect.Struct {\n\t\treturn tbl\n\t}\n\tif value.NumField() == 0 {\n\t\treturn tbl\n\t}\n\tfirstfield := value.Field(0)\n\tfirstfieldType := typ.Field(0)\n\tif !firstfield.CanInterface() {\n\t\treturn tbl\n\t}\n\t_, ok := firstfield.Interface().(TableStruct)\n\tif !ok {\n\t\treturn tbl\n\t}\n\tif !firstfield.CanSet() {\n\t\treturn tbl\n\t}\n\ttag := firstfieldType.Tag.Get(\"sq\")\n\ttableSchema, tableName, ok := strings.Cut(tag, \".\")\n\tif !ok {\n\t\ttableSchema, tableName = \"\", tableSchema\n\t}\n\tif tableName == \"\" {\n\t\ttableName = strings.ToLower(typ.Name())\n\t}\n\ttableStruct := NewTableStruct(tableSchema, tableName, alias)\n\tfirstfield.Set(reflect.ValueOf(tableStruct))\n\tfor i := 1; i < value.NumField(); i++ {\n\t\tv := value.Field(i)\n\t\tif !v.CanInterface() {\n\t\t\tcontinue\n\t\t}\n\t\tif !v.CanSet() {\n\t\t\tcontinue\n\t\t}\n\t\tfieldType := typ.Field(i)\n\t\tname := fieldType.Tag.Get(\"sq\")\n\t\tif name == \"\" {\n\t\t\tname = strings.ToLower(fieldType.Name)\n\t\t}\n\t\tswitch v.Interface().(type) {\n\t\tcase AnyField:\n\t\t\tv.Set(reflect.ValueOf(NewAnyField(name, tableStruct)))\n\t\tcase ArrayField:\n\t\t\tv.Set(reflect.ValueOf(NewArrayField(name, tableStruct)))\n\t\tcase BinaryField:\n\t\t\tv.Set(reflect.ValueOf(NewBinaryField(name, tableStruct)))\n\t\tcase BooleanField:\n\t\t\tv.Set(reflect.ValueOf(NewBooleanField(name, tableStruct)))\n\t\tcase EnumField:\n\t\t\tv.Set(reflect.ValueOf(NewEnumField(name, tableStruct)))\n\t\tcase JSONField:\n\t\t\tv.Set(reflect.ValueOf(NewJSONField(name, tableStruct)))\n\t\tcase NumberField:\n\t\t\tv.Set(reflect.ValueOf(NewNumberField(name, tableStruct)))\n\t\tcase StringField:\n\t\t\tv.Set(reflect.ValueOf(NewStringField(name, tableStruct)))\n\t\tcase TimeField:\n\t\t\tv.Set(reflect.ValueOf(NewTimeField(name, tableStruct)))\n\t\tcase UUIDField:\n\t\t\tv.Set(reflect.ValueOf(NewUUIDField(name, tableStruct)))\n\t\t}\n\t}\n\treturn tbl\n}\n\nfunc writeFieldIdentifier(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, table TableStruct, fieldName string) {\n\ttableQualifier, _, _ := strings.Cut(table.alias, \"(\")\n\ttableQualifier = strings.TrimRight(tableQualifier, \" \")\n\tif tableQualifier == \"\" {\n\t\ttableQualifier = table.name\n\t}\n\tif tableQualifier != \"\" {\n\t\tbuf.WriteString(QuoteIdentifier(dialect, tableQualifier) + \".\")\n\t}\n\tbuf.WriteString(QuoteIdentifier(dialect, fieldName))\n}\n\nfunc writeFieldOrder(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, desc, nullsfirst sql.NullBool) {\n\tif desc.Valid {\n\t\tif desc.Bool {\n\t\t\tbuf.WriteString(\" DESC\")\n\t\t} else {\n\t\t\tbuf.WriteString(\" ASC\")\n\t\t}\n\t}\n\tif nullsfirst.Valid {\n\t\tif nullsfirst.Bool {\n\t\t\tbuf.WriteString(\" NULLS FIRST\")\n\t\t} else {\n\t\t\tbuf.WriteString(\" NULLS LAST\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "fields_test.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql/driver\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n\t\"github.com/google/uuid\"\n)\n\nfunc TestTableStruct(t *testing.T) {\n\tt.Parallel()\n\ttbl := NewTableStruct(\"public\", \"users\", \"u\")\n\tif diff := testutil.Diff(tbl.GetAlias(), \"u\"); diff != \"\" {\n\t\tt.Error(testutil.Callers(), diff)\n\t}\n\tgotQuery, _, err := ToSQL(\"\", tbl, nil)\n\tif err != nil {\n\t\tt.Error(testutil.Callers(), err)\n\t}\n\tif diff := testutil.Diff(gotQuery, \"public.users\"); diff != \"\" {\n\t\tt.Error(testutil.Callers(), diff)\n\t}\n}\n\nfunc TestArrayField(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\ttbl := NewTableStruct(\"\", \"tbl\", \"\")\n\t\tf1 := NewArrayField(\"field\", tbl).As(\"f\")\n\t\tif diff := testutil.Diff(f1.GetAlias(), \"f\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"alias brackets removed\", func(t *testing.T) {\n\t\ttbl := NewTableStruct(\"\", \"tbl\", \"t (id, name, email)\")\n\t\tf1 := NewArrayField(\"field\", tbl)\n\t\tgotQuery, _, err := ToSQL(\"\", f1, nil)\n\t\tif err != nil {\n\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t}\n\t\tif diff := testutil.Diff(tbl.GetAlias(), \"t (id, name, email)\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif diff := testutil.Diff(gotQuery, \"t.field\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tfield := NewArrayField(\"field\", NewTableStruct(\"\", \"tbl\", \"\"))\n\ttests := []TestTable{{\n\t\tdescription: \"IsNull\", item: field.IsNull(),\n\t\twantQuery: \"tbl.field IS NULL\",\n\t}, {\n\t\tdescription: \"IsNotNull\", item: field.IsNotNull(),\n\t\twantQuery: \"tbl.field IS NOT NULL\",\n\t}, {\n\t\tdescription: \"Set\", item: field.Set(Expr(\"NULL\")),\n\t\twantQuery: \"field = NULL\",\n\t}, {\n\t\tdescription: \"Set\", item: field.SetArray([]int{1, 2, 3}),\n\t\twantQuery: \"field = ?\",\n\t\twantArgs:  []any{`[1,2,3]`},\n\t}, {\n\t\tdescription: \"Setf\", item: field.Setf(\"VALUES({})\", field.WithPrefix(\"\")),\n\t\twantQuery: \"field = VALUES(field)\",\n\t}, {\n\t\tdescription: \"Set EXCLUDED\", item: field.Set(field.WithPrefix(\"EXCLUDED\")),\n\t\twantQuery: \"field = EXCLUDED.field\",\n\t}, {\n\t\tdescription: \"Set self\", item: field.Set(field), dialect: DialectMySQL,\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Set with alias\", item: field.Set(field.WithPrefix(\"new\")),\n\t\twantQuery: \"field = new.field\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\nfunc TestBinaryField(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\ttbl := NewTableStruct(\"\", \"tbl\", \"\")\n\t\tf1 := NewBinaryField(\"field\", tbl).As(\"f\")\n\t\tif diff := testutil.Diff(f1.GetAlias(), \"f\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tfield := NewBinaryField(\"field\", NewTableStruct(\"\", \"tbl\", \"\"))\n\ttests := []TestTable{{\n\t\tdescription: \"IsNull\", item: field.IsNull(),\n\t\twantQuery: \"tbl.field IS NULL\",\n\t}, {\n\t\tdescription: \"IsNotNull\", item: field.IsNotNull(),\n\t\twantQuery: \"tbl.field IS NOT NULL\",\n\t}, {\n\t\tdescription: \"Asc NullsLast\", item: field.Asc().NullsLast(),\n\t\twantQuery: \"tbl.field ASC NULLS LAST\",\n\t}, {\n\t\tdescription: \"Desc NullsFirst\", item: field.Desc().NullsFirst(),\n\t\twantQuery: \"tbl.field DESC NULLS FIRST\",\n\t}, {\n\t\tdescription: \"Eq\", item: field.Eq(field),\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Ne\", item: field.Ne(field),\n\t\twantQuery: \"tbl.field <> tbl.field\",\n\t}, {\n\t\tdescription: \"EqBytes\", item: field.EqBytes([]byte{0xff, 0xff}),\n\t\twantQuery: \"tbl.field = ?\", wantArgs: []any{[]byte{0xff, 0xff}},\n\t}, {\n\t\tdescription: \"NeBytes\", item: field.NeBytes([]byte{0xff, 0xff}),\n\t\twantQuery: \"tbl.field <> ?\", wantArgs: []any{[]byte{0xff, 0xff}},\n\t}, {\n\t\tdescription: \"Set\", item: field.Set(Expr(\"NULL\")),\n\t\twantQuery: \"field = NULL\",\n\t}, {\n\t\tdescription: \"Setf\", item: field.Setf(\"VALUES({})\", field.WithPrefix(\"\")),\n\t\twantQuery: \"field = VALUES(field)\",\n\t}, {\n\t\tdescription: \"Set EXCLUDED\", item: field.Set(field.WithPrefix(\"EXCLUDED\")),\n\t\twantQuery: \"field = EXCLUDED.field\",\n\t}, {\n\t\tdescription: \"Set self\", item: field.Set(field), dialect: DialectMySQL,\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Set with alias\", item: field.Set(field.WithPrefix(\"new\")),\n\t\twantQuery: \"field = new.field\",\n\t}, {\n\t\tdescription: \"SetBytes\", item: field.SetBytes([]byte{0xff, 0xff}),\n\t\twantQuery: \"field = ?\", wantArgs: []any{[]byte{0xff, 0xff}},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\nfunc TestBooleanField(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\ttbl := NewTableStruct(\"\", \"tbl\", \"\")\n\t\tf1 := NewBooleanField(\"field\", tbl).As(\"f\")\n\t\tif diff := testutil.Diff(f1.GetAlias(), \"f\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tfield := NewBooleanField(\"field\", NewTableStruct(\"\", \"tbl\", \"\"))\n\ttests := []TestTable{{\n\t\tdescription: \"IsNull\", item: field.IsNull(),\n\t\twantQuery: \"tbl.field IS NULL\",\n\t}, {\n\t\tdescription: \"IsNotNull\", item: field.IsNotNull(),\n\t\twantQuery: \"tbl.field IS NOT NULL\",\n\t}, {\n\t\tdescription: \"Asc NullsLast\", item: field.Asc().NullsLast(),\n\t\twantQuery: \"tbl.field ASC NULLS LAST\",\n\t}, {\n\t\tdescription: \"Desc NullsFirst\", item: field.Desc().NullsFirst(),\n\t\twantQuery: \"tbl.field DESC NULLS FIRST\",\n\t}, {\n\t\tdescription: \"Eq\", item: field.Eq(field),\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Ne\", item: field.Ne(field),\n\t\twantQuery: \"tbl.field <> tbl.field\",\n\t}, {\n\t\tdescription: \"EqBytes\", item: field.EqBool(true),\n\t\twantQuery: \"tbl.field = ?\", wantArgs: []any{true},\n\t}, {\n\t\tdescription: \"NeBytes\", item: field.NeBool(true),\n\t\twantQuery: \"tbl.field <> ?\", wantArgs: []any{true},\n\t}, {\n\t\tdescription: \"Set\", item: field.Set(Expr(\"NULL\")),\n\t\twantQuery: \"field = NULL\",\n\t}, {\n\t\tdescription: \"Setf\", item: field.Setf(\"VALUES({})\", field.WithPrefix(\"\")),\n\t\twantQuery: \"field = VALUES(field)\",\n\t}, {\n\t\tdescription: \"Set EXCLUDED\", item: field.Set(field.WithPrefix(\"EXCLUDED\")),\n\t\twantQuery: \"field = EXCLUDED.field\",\n\t}, {\n\t\tdescription: \"Set self\", item: field.Set(field), dialect: DialectMySQL,\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Set with alias\", item: field.Set(field.WithPrefix(\"new\")),\n\t\twantQuery: \"field = new.field\",\n\t}, {\n\t\tdescription: \"SetBool\", item: field.SetBool(true),\n\t\twantQuery: \"field = ?\", wantArgs: []any{true},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\nfunc TestCustomField(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\ttbl := NewTableStruct(\"\", \"tbl\", \"\")\n\t\tf1 := NewAnyField(\"field\", tbl).As(\"f\")\n\t\tif diff := testutil.Diff(f1.GetAlias(), \"f\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tfield := NewAnyField(\"field\", NewTableStruct(\"\", \"tbl\", \"\"))\n\ttests := []TestTable{{\n\t\tdescription: \"IsNull\", item: field.IsNull(),\n\t\twantQuery: \"tbl.field IS NULL\",\n\t}, {\n\t\tdescription: \"IsNotNull\", item: field.IsNotNull(),\n\t\twantQuery: \"tbl.field IS NOT NULL\",\n\t}, {\n\t\tdescription: \"Asc NullsLast\", item: field.Asc().NullsLast(),\n\t\twantQuery: \"tbl.field ASC NULLS LAST\",\n\t}, {\n\t\tdescription: \"Desc NullsFirst\", item: field.Desc().NullsFirst(),\n\t\twantQuery: \"tbl.field DESC NULLS FIRST\",\n\t}, {\n\t\tdescription: \"In\", item: field.In(RowValue{1, 2, 3}),\n\t\twantQuery: \"tbl.field IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"NotIn\", item: field.NotIn(RowValue{1, 2, 3}),\n\t\twantQuery: \"tbl.field NOT IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"Eq\", item: field.Eq(field),\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Ne\", item: field.Ne(field),\n\t\twantQuery: \"tbl.field <> tbl.field\",\n\t}, {\n\t\tdescription: \"Lt\", item: field.Lt(field),\n\t\twantQuery: \"tbl.field < tbl.field\",\n\t}, {\n\t\tdescription: \"Le\", item: field.Le(field),\n\t\twantQuery: \"tbl.field <= tbl.field\",\n\t}, {\n\t\tdescription: \"Gt\", item: field.Gt(field),\n\t\twantQuery: \"tbl.field > tbl.field\",\n\t}, {\n\t\tdescription: \"Ge\", item: field.Ge(field),\n\t\twantQuery: \"tbl.field >= tbl.field\",\n\t}, {\n\t\tdescription: \"Expr\", item: field.Expr(\"&& ARRAY[1, 2, 3]\"),\n\t\twantQuery: \"tbl.field && ARRAY[1, 2, 3]\",\n\t}, {\n\t\tdescription: \"Set\", item: field.Set(Expr(\"NULL\")),\n\t\twantQuery: \"field = NULL\",\n\t}, {\n\t\tdescription: \"Setf\", item: field.Setf(\"VALUES({})\", field.WithPrefix(\"\")),\n\t\twantQuery: \"field = VALUES(field)\",\n\t}, {\n\t\tdescription: \"Set EXCLUDED\", item: field.Set(field.WithPrefix(\"EXCLUDED\")),\n\t\twantQuery: \"field = EXCLUDED.field\",\n\t}, {\n\t\tdescription: \"Set Self\", item: field.Set(field), dialect: DialectMySQL,\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Set with alias\", item: field.Set(field.WithPrefix(\"new\")),\n\t\twantQuery: \"field = new.field\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\nfunc TestEnumField(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\ttbl := NewTableStruct(\"\", \"tbl\", \"\")\n\t\tf1 := NewEnumField(\"field\", tbl).As(\"f\")\n\t\tif diff := testutil.Diff(f1.GetAlias(), \"f\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tfield := NewEnumField(\"field\", NewTableStruct(\"\", \"tbl\", \"\"))\n\ttests := []TestTable{{\n\t\tdescription: \"IsNull\", item: field.IsNull(),\n\t\twantQuery: \"tbl.field IS NULL\",\n\t}, {\n\t\tdescription: \"IsNotNull\", item: field.IsNotNull(),\n\t\twantQuery: \"tbl.field IS NOT NULL\",\n\t}, {\n\t\tdescription: \"In\", item: field.In(RowValue{1, 2, 3}),\n\t\twantQuery: \"tbl.field IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"NotIn\", item: field.NotIn(RowValue{1, 2, 3}),\n\t\twantQuery: \"tbl.field NOT IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"Eq\", item: field.Eq(field),\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Ne\", item: field.Ne(field),\n\t\twantQuery: \"tbl.field <> tbl.field\",\n\t}, {\n\t\tdescription: \"EqEnum\", item: field.Eq(Monday),\n\t\twantQuery: \"tbl.field = ?\",\n\t\twantArgs:  []any{\"Monday\"},\n\t}, {\n\t\tdescription: \"NeEnum\", item: field.Ne(Monday),\n\t\twantQuery: \"tbl.field <> ?\",\n\t\twantArgs:  []any{\"Monday\"},\n\t}, {\n\t\tdescription: \"Set\", item: field.Set(Expr(\"NULL\")),\n\t\twantQuery: \"field = NULL\",\n\t}, {\n\t\tdescription: \"SetEnum\", item: field.Set(Monday),\n\t\twantQuery: \"field = ?\",\n\t\twantArgs:  []any{\"Monday\"},\n\t}, {\n\t\tdescription: \"Setf\", item: field.Setf(\"VALUES({})\", field.WithPrefix(\"\")),\n\t\twantQuery: \"field = VALUES(field)\",\n\t}, {\n\t\tdescription: \"Set EXCLUDED\", item: field.Set(field.WithPrefix(\"EXCLUDED\")),\n\t\twantQuery: \"field = EXCLUDED.field\",\n\t}, {\n\t\tdescription: \"Set self\", item: field.Set(field), dialect: DialectMySQL,\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Set with alias\", item: field.Set(field.WithPrefix(\"new\")),\n\t\twantQuery: \"field = new.field\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\nfunc TestJSONField(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\ttbl := NewTableStruct(\"\", \"tbl\", \"\")\n\t\tf1 := NewJSONField(\"field\", tbl).As(\"f\")\n\t\tif diff := testutil.Diff(f1.GetAlias(), \"f\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\ttype Data struct {\n\t\tID   int    `json:\"id\"`\n\t\tName string `json:\"name\"`\n\t}\n\n\tfield := NewJSONField(\"field\", NewTableStruct(\"\", \"tbl\", \"\"))\n\ttests := []TestTable{{\n\t\tdescription: \"IsNull\", item: field.IsNull(),\n\t\twantQuery: \"tbl.field IS NULL\",\n\t}, {\n\t\tdescription: \"IsNotNull\", item: field.IsNotNull(),\n\t\twantQuery: \"tbl.field IS NOT NULL\",\n\t}, {\n\t\tdescription: \"Set\", item: field.Set(Expr(\"NULL\")),\n\t\twantQuery: \"field = NULL\",\n\t}, {\n\t\tdescription: \"Set\", item: field.SetJSON([]int{1, 2, 3}),\n\t\twantQuery: \"field = ?\",\n\t\twantArgs:  []any{`[1,2,3]`},\n\t}, {\n\t\tdescription: \"Setf\", item: field.Setf(\"VALUES({})\", field.WithPrefix(\"\")),\n\t\twantQuery: \"field = VALUES(field)\",\n\t}, {\n\t\tdescription: \"Set EXCLUDED\", item: field.Set(field.WithPrefix(\"EXCLUDED\")),\n\t\twantQuery: \"field = EXCLUDED.field\",\n\t}, {\n\t\tdescription: \"Set self\", item: field.Set(field), dialect: DialectMySQL,\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Set with alias\", item: field.Set(field.WithPrefix(\"new\")),\n\t\twantQuery: \"field = new.field\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\nfunc TestNumberField(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\ttbl := NewTableStruct(\"\", \"tbl\", \"\")\n\t\tf1 := NewNumberField(\"field\", tbl).As(\"f\")\n\t\tif diff := testutil.Diff(f1.GetAlias(), \"f\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tfield := NewNumberField(\"field\", NewTableStruct(\"\", \"tbl\", \"\"))\n\ttests := []TestTable{{\n\t\tdescription: \"IsNull\", item: field.IsNull(),\n\t\twantQuery: \"tbl.field IS NULL\",\n\t}, {\n\t\tdescription: \"IsNotNull\", item: field.IsNotNull(),\n\t\twantQuery: \"tbl.field IS NOT NULL\",\n\t}, {\n\t\tdescription: \"Asc NullsLast\", item: field.Asc().NullsLast(),\n\t\twantQuery: \"tbl.field ASC NULLS LAST\",\n\t}, {\n\t\tdescription: \"Desc NullsFirst\", item: field.Desc().NullsFirst(),\n\t\twantQuery: \"tbl.field DESC NULLS FIRST\",\n\t}, {\n\t\tdescription: \"In\", item: field.In(RowValue{1, 2, 3}),\n\t\twantQuery: \"tbl.field IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"NotIn\", item: field.NotIn(RowValue{1, 2, 3}),\n\t\twantQuery: \"tbl.field NOT IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"Eq\", item: field.Eq(field),\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Ne\", item: field.Ne(field),\n\t\twantQuery: \"tbl.field <> tbl.field\",\n\t}, {\n\t\tdescription: \"Lt\", item: field.Lt(field),\n\t\twantQuery: \"tbl.field < tbl.field\",\n\t}, {\n\t\tdescription: \"Le\", item: field.Le(field),\n\t\twantQuery: \"tbl.field <= tbl.field\",\n\t}, {\n\t\tdescription: \"Gt\", item: field.Gt(field),\n\t\twantQuery: \"tbl.field > tbl.field\",\n\t}, {\n\t\tdescription: \"Ge\", item: field.Ge(field),\n\t\twantQuery: \"tbl.field >= tbl.field\",\n\t}, {\n\t\tdescription: \"EqInt\", item: field.EqInt(3),\n\t\twantQuery: \"tbl.field = ?\", wantArgs: []any{3},\n\t}, {\n\t\tdescription: \"NeInt\", item: field.NeInt(3),\n\t\twantQuery: \"tbl.field <> ?\", wantArgs: []any{3},\n\t}, {\n\t\tdescription: \"LtInt\", item: field.LtInt(3),\n\t\twantQuery: \"tbl.field < ?\", wantArgs: []any{3},\n\t}, {\n\t\tdescription: \"LeInt\", item: field.LeInt(3),\n\t\twantQuery: \"tbl.field <= ?\", wantArgs: []any{3},\n\t}, {\n\t\tdescription: \"GtInt\", item: field.GtInt(3),\n\t\twantQuery: \"tbl.field > ?\", wantArgs: []any{3},\n\t}, {\n\t\tdescription: \"GeInt\", item: field.GeInt(3),\n\t\twantQuery: \"tbl.field >= ?\", wantArgs: []any{3},\n\t}, {\n\t\tdescription: \"EqInt64\", item: field.EqInt64(5),\n\t\twantQuery: \"tbl.field = ?\", wantArgs: []any{int64(5)},\n\t}, {\n\t\tdescription: \"NeInt64\", item: field.NeInt64(5),\n\t\twantQuery: \"tbl.field <> ?\", wantArgs: []any{int64(5)},\n\t}, {\n\t\tdescription: \"LtInt64\", item: field.LtInt64(5),\n\t\twantQuery: \"tbl.field < ?\", wantArgs: []any{int64(5)},\n\t}, {\n\t\tdescription: \"LeInt64\", item: field.LeInt64(5),\n\t\twantQuery: \"tbl.field <= ?\", wantArgs: []any{int64(5)},\n\t}, {\n\t\tdescription: \"GtInt64\", item: field.GtInt64(5),\n\t\twantQuery: \"tbl.field > ?\", wantArgs: []any{int64(5)},\n\t}, {\n\t\tdescription: \"GeInt64\", item: field.GeInt64(5),\n\t\twantQuery: \"tbl.field >= ?\", wantArgs: []any{int64(5)},\n\t}, {\n\t\tdescription: \"EqFloat64\", item: field.EqFloat64(7.11),\n\t\twantQuery: \"tbl.field = ?\", wantArgs: []any{float64(7.11)},\n\t}, {\n\t\tdescription: \"NeFloat64\", item: field.NeFloat64(7.11),\n\t\twantQuery: \"tbl.field <> ?\", wantArgs: []any{float64(7.11)},\n\t}, {\n\t\tdescription: \"LtFloat64\", item: field.LtFloat64(7.11),\n\t\twantQuery: \"tbl.field < ?\", wantArgs: []any{float64(7.11)},\n\t}, {\n\t\tdescription: \"LeFloat64\", item: field.LeFloat64(7.11),\n\t\twantQuery: \"tbl.field <= ?\", wantArgs: []any{float64(7.11)},\n\t}, {\n\t\tdescription: \"GtFloat64\", item: field.GtFloat64(7.11),\n\t\twantQuery: \"tbl.field > ?\", wantArgs: []any{float64(7.11)},\n\t}, {\n\t\tdescription: \"GeFloat64\", item: field.GeFloat64(7.11),\n\t\twantQuery: \"tbl.field >= ?\", wantArgs: []any{float64(7.11)},\n\t}, {\n\t\tdescription: \"Set\", item: field.Set(Expr(\"NULL\")),\n\t\twantQuery: \"field = NULL\",\n\t}, {\n\t\tdescription: \"Setf\", item: field.Setf(\"VALUES({})\", field.WithPrefix(\"\")),\n\t\twantQuery: \"field = VALUES(field)\",\n\t}, {\n\t\tdescription: \"Set EXCLUDED\", item: field.Set(field.WithPrefix(\"EXCLUDED\")),\n\t\twantQuery: \"field = EXCLUDED.field\",\n\t}, {\n\t\tdescription: \"Set self\", item: field.Set(field), dialect: DialectMySQL,\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Set with alias\", item: field.Set(field.WithPrefix(\"new\")),\n\t\twantQuery: \"field = new.field\",\n\t}, {\n\t\tdescription: \"SetInt\", item: field.SetInt(3),\n\t\twantQuery: \"field = ?\", wantArgs: []any{3},\n\t}, {\n\t\tdescription: \"SetInt64\", item: field.SetInt64(5),\n\t\twantQuery: \"field = ?\", wantArgs: []any{int64(5)},\n\t}, {\n\t\tdescription: \"SetFloat64\", item: field.SetFloat64(7.11),\n\t\twantQuery: \"field = ?\", wantArgs: []any{float64(7.11)},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\nfunc TestStringField(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\ttbl := NewTableStruct(\"\", \"tbl\", \"\")\n\t\tf1 := NewStringField(\"field\", tbl).As(\"f\")\n\t\tif diff := testutil.Diff(f1.GetAlias(), \"f\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tfield := NewStringField(\"field\", NewTableStruct(\"\", \"tbl\", \"\"))\n\ttests := []TestTable{{\n\t\tdescription: \"IsNull\", item: field.IsNull(),\n\t\twantQuery: \"tbl.field IS NULL\",\n\t}, {\n\t\tdescription: \"IsNotNull\", item: field.IsNotNull(),\n\t\twantQuery: \"tbl.field IS NOT NULL\",\n\t}, {\n\t\tdescription: \"Asc NullsLast\", item: field.Asc().NullsLast(),\n\t\twantQuery: \"tbl.field ASC NULLS LAST\",\n\t}, {\n\t\tdescription: \"Desc NullsFirst\", item: field.Desc().NullsFirst(),\n\t\twantQuery: \"tbl.field DESC NULLS FIRST\",\n\t}, {\n\t\tdescription: \"In\", item: field.In(RowValue{1, 2, 3}),\n\t\twantQuery: \"tbl.field IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"NotIn\", item: field.NotIn(RowValue{1, 2, 3}),\n\t\twantQuery: \"tbl.field NOT IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"Eq\", item: field.Eq(field),\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Ne\", item: field.Ne(field),\n\t\twantQuery: \"tbl.field <> tbl.field\",\n\t}, {\n\t\tdescription: \"Lt\", item: field.Lt(field),\n\t\twantQuery: \"tbl.field < tbl.field\",\n\t}, {\n\t\tdescription: \"Le\", item: field.Le(field),\n\t\twantQuery: \"tbl.field <= tbl.field\",\n\t}, {\n\t\tdescription: \"Gt\", item: field.Gt(field),\n\t\twantQuery: \"tbl.field > tbl.field\",\n\t}, {\n\t\tdescription: \"Ge\", item: field.Ge(field),\n\t\twantQuery: \"tbl.field >= tbl.field\",\n\t}, {\n\t\tdescription: \"EqString\", item: field.EqString(\"lorem ipsum\"),\n\t\twantQuery: \"tbl.field = ?\", wantArgs: []any{\"lorem ipsum\"},\n\t}, {\n\t\tdescription: \"NeString\", item: field.NeString(\"lorem ipsum\"),\n\t\twantQuery: \"tbl.field <> ?\", wantArgs: []any{\"lorem ipsum\"},\n\t}, {\n\t\tdescription: \"LtString\", item: field.LtString(\"lorem ipsum\"),\n\t\twantQuery: \"tbl.field < ?\", wantArgs: []any{\"lorem ipsum\"},\n\t}, {\n\t\tdescription: \"LeString\", item: field.LeString(\"lorem ipsum\"),\n\t\twantQuery: \"tbl.field <= ?\", wantArgs: []any{\"lorem ipsum\"},\n\t}, {\n\t\tdescription: \"GtString\", item: field.GtString(\"lorem ipsum\"),\n\t\twantQuery: \"tbl.field > ?\", wantArgs: []any{\"lorem ipsum\"},\n\t}, {\n\t\tdescription: \"GeString\", item: field.GeString(\"lorem ipsum\"),\n\t\twantQuery: \"tbl.field >= ?\", wantArgs: []any{\"lorem ipsum\"},\n\t}, {\n\t\tdescription: \"LikeString\", item: field.LikeString(\"lorem%\"),\n\t\twantQuery: \"tbl.field LIKE ?\", wantArgs: []any{\"lorem%\"},\n\t}, {\n\t\tdescription: \"NotLikeString\", item: field.NotLikeString(\"lorem%\"),\n\t\twantQuery: \"tbl.field NOT LIKE ?\", wantArgs: []any{\"lorem%\"},\n\t}, {\n\t\tdescription: \"ILikeString\", item: field.ILikeString(\"lorem%\"),\n\t\twantQuery: \"tbl.field ILIKE ?\", wantArgs: []any{\"lorem%\"},\n\t}, {\n\t\tdescription: \"NotILikeString\", item: field.NotILikeString(\"lorem%\"),\n\t\twantQuery: \"tbl.field NOT ILIKE ?\", wantArgs: []any{\"lorem%\"},\n\t}, {\n\t\tdescription: \"Set\", item: field.Set(Expr(\"NULL\")),\n\t\twantQuery: \"field = NULL\",\n\t}, {\n\t\tdescription: \"Setf\", item: field.Setf(\"VALUES({})\", field.WithPrefix(\"\")),\n\t\twantQuery: \"field = VALUES(field)\",\n\t}, {\n\t\tdescription: \"Set EXCLUDED\", item: field.Set(field.WithPrefix(\"EXCLUDED\")),\n\t\twantQuery: \"field = EXCLUDED.field\",\n\t}, {\n\t\tdescription: \"Set self\", item: field.Set(field), dialect: DialectMySQL,\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Set with alias\", item: field.Set(field.WithPrefix(\"new\")),\n\t\twantQuery: \"field = new.field\",\n\t}, {\n\t\tdescription: \"SetString\", item: field.SetString(\"lorem ipsum\"),\n\t\twantQuery: \"field = ?\", wantArgs: []any{\"lorem ipsum\"},\n\t}, {\n\t\tdescription: \"postgres Collate\", item: field.Collate(\"c\").LtString(\"lorem ipsum\"),\n\t\tdialect:   DialectPostgres,\n\t\twantQuery: `tbl.field COLLATE \"c\" < $1`, wantArgs: []any{\"lorem ipsum\"},\n\t}, {\n\t\tdescription: \"mysql Collate\", item: field.Collate(\"latin1_swedish_ci\").LtString(\"lorem ipsum\"),\n\t\twantQuery: \"tbl.field COLLATE latin1_swedish_ci < ?\", wantArgs: []any{\"lorem ipsum\"},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\nfunc TestTimeField(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\ttbl := NewTableStruct(\"\", \"tbl\", \"\")\n\t\tf1 := NewTimeField(\"field\", tbl).As(\"f\")\n\t\tif diff := testutil.Diff(f1.GetAlias(), \"f\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tfield := NewTimeField(\"field\", NewTableStruct(\"\", \"tbl\", \"\"))\n\tzeroTime := time.Unix(0, 0).UTC()\n\ttests := []TestTable{{\n\t\tdescription: \"IsNull\", item: field.IsNull(),\n\t\twantQuery: \"tbl.field IS NULL\",\n\t}, {\n\t\tdescription: \"IsNotNull\", item: field.IsNotNull(),\n\t\twantQuery: \"tbl.field IS NOT NULL\",\n\t}, {\n\t\tdescription: \"Asc NullsLast\", item: field.Asc().NullsLast(),\n\t\twantQuery: \"tbl.field ASC NULLS LAST\",\n\t}, {\n\t\tdescription: \"Desc NullsFirst\", item: field.Desc().NullsFirst(),\n\t\twantQuery: \"tbl.field DESC NULLS FIRST\",\n\t}, {\n\t\tdescription: \"In\", item: field.In(RowValue{1, 2, 3}),\n\t\twantQuery: \"tbl.field IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"NotIn\", item: field.NotIn(RowValue{1, 2, 3}),\n\t\twantQuery: \"tbl.field NOT IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"Eq\", item: field.Eq(field),\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Ne\", item: field.Ne(field),\n\t\twantQuery: \"tbl.field <> tbl.field\",\n\t}, {\n\t\tdescription: \"Lt\", item: field.Lt(field),\n\t\twantQuery: \"tbl.field < tbl.field\",\n\t}, {\n\t\tdescription: \"Le\", item: field.Le(field),\n\t\twantQuery: \"tbl.field <= tbl.field\",\n\t}, {\n\t\tdescription: \"Gt\", item: field.Gt(field),\n\t\twantQuery: \"tbl.field > tbl.field\",\n\t}, {\n\t\tdescription: \"Ge\", item: field.Ge(field),\n\t\twantQuery: \"tbl.field >= tbl.field\",\n\t}, {\n\t\tdescription: \"EqTime\", item: field.EqTime(zeroTime),\n\t\twantQuery: \"tbl.field = ?\", wantArgs: []any{zeroTime},\n\t}, {\n\t\tdescription: \"NeTime\", item: field.NeTime(zeroTime),\n\t\twantQuery: \"tbl.field <> ?\", wantArgs: []any{zeroTime},\n\t}, {\n\t\tdescription: \"LtTime\", item: field.LtTime(zeroTime),\n\t\twantQuery: \"tbl.field < ?\", wantArgs: []any{zeroTime},\n\t}, {\n\t\tdescription: \"LeTime\", item: field.LeTime(zeroTime),\n\t\twantQuery: \"tbl.field <= ?\", wantArgs: []any{zeroTime},\n\t}, {\n\t\tdescription: \"GtTime\", item: field.GtTime(zeroTime),\n\t\twantQuery: \"tbl.field > ?\", wantArgs: []any{zeroTime},\n\t}, {\n\t\tdescription: \"GeTime\", item: field.GeTime(zeroTime),\n\t\twantQuery: \"tbl.field >= ?\", wantArgs: []any{zeroTime},\n\t}, {\n\t\tdescription: \"Set\", item: field.Set(Expr(\"NULL\")),\n\t\twantQuery: \"field = NULL\",\n\t}, {\n\t\tdescription: \"Setf\", item: field.Setf(\"VALUES({})\", field.WithPrefix(\"\")),\n\t\twantQuery: \"field = VALUES(field)\",\n\t}, {\n\t\tdescription: \"Set EXCLUDED\", item: field.Set(field.WithPrefix(\"EXCLUDED\")),\n\t\twantQuery: \"field = EXCLUDED.field\",\n\t}, {\n\t\tdescription: \"Set self\", item: field.Set(field), dialect: DialectMySQL,\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Set with alias\", item: field.Set(field.WithPrefix(\"new\")),\n\t\twantQuery: \"field = new.field\",\n\t}, {\n\t\tdescription: \"SetTime\", item: field.SetTime(zeroTime),\n\t\twantQuery: \"field = ?\", wantArgs: []any{zeroTime},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\nfunc TestTimestamp(t *testing.T) {\n\tt.Run(\"Value\", func(t *testing.T) {\n\t\ttype TestTable struct {\n\t\t\tdescription string\n\t\t\ttimestamp   Timestamp\n\t\t\twantValue   driver.Value\n\t\t}\n\n\t\ttests := []TestTable{{\n\t\t\tdescription: \"empty\",\n\t\t\ttimestamp:   Timestamp{},\n\t\t\twantValue:   nil,\n\t\t}, {\n\t\t\tdescription: \"sqlite\",\n\t\t\ttimestamp: Timestamp{\n\t\t\t\tValid:   true,\n\t\t\t\tTime:    time.Unix(1, 0),\n\t\t\t\tdialect: DialectSQLite,\n\t\t\t},\n\t\t\twantValue: time.Unix(1, 0).Unix(),\n\t\t}, {\n\t\t\tdescription: \"non-sqlite\",\n\t\t\ttimestamp: Timestamp{\n\t\t\t\tValid:   true,\n\t\t\t\tTime:    time.Unix(1, 0),\n\t\t\t\tdialect: DialectPostgres,\n\t\t\t},\n\t\t\twantValue: time.Unix(1, 0),\n\t\t}}\n\n\t\tfor _, tt := range tests {\n\t\t\ttt := tt\n\t\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tgotValue, err := tt.timestamp.Value()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t\t}\n\t\t\t\tif diff := testutil.Diff(gotValue, tt.wantValue); diff != \"\" {\n\t\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"Scan\", func(t *testing.T) {\n\t\ttype TestTable struct {\n\t\t\tdescription   string\n\t\t\tvalue         any\n\t\t\twantTimestamp Timestamp\n\t\t}\n\n\t\tparseTime := func(value string) time.Time {\n\t\t\tvalue = strings.TrimSuffix(value, \"Z\")\n\t\t\tfor _, format := range timestampFormats {\n\t\t\t\tif timeVal, err := time.ParseInLocation(format, value, time.UTC); err == nil {\n\t\t\t\t\treturn timeVal\n\t\t\t\t}\n\t\t\t}\n\t\t\tt.Fatalf(testutil.Callers()+\" could not convert %q into time\", value)\n\t\t\treturn time.Time{}\n\t\t}\n\n\t\ttests := []TestTable{{\n\t\t\tdescription:   \"empty\",\n\t\t\tvalue:         nil,\n\t\t\twantTimestamp: Timestamp{},\n\t\t}, {\n\t\t\tdescription:   \"empty string\",\n\t\t\tvalue:         \"\",\n\t\t\twantTimestamp: Timestamp{},\n\t\t}, {\n\t\t\tdescription:   \"empty bytes\",\n\t\t\tvalue:         []byte{},\n\t\t\twantTimestamp: Timestamp{},\n\t\t}, {\n\t\t\tdescription:   \"time.Time\",\n\t\t\tvalue:         time.Unix(1, 0),\n\t\t\twantTimestamp: NewTimestamp(time.Unix(1, 0)),\n\t\t}, {\n\t\t\tdescription:   \"time.Time\",\n\t\t\tvalue:         time.Unix(1, 0),\n\t\t\twantTimestamp: NewTimestamp(time.Unix(1, 0)),\n\t\t}, {\n\t\t\tdescription:   \"2006-01-02 15:04:05\",\n\t\t\tvalue:         \"2006-01-02 15:04:05\",\n\t\t\twantTimestamp: NewTimestamp(parseTime(\"2006-01-02 15:04:05\")),\n\t\t}, {\n\t\t\tdescription:   \"2006-01-02 15:04:05Z\",\n\t\t\tvalue:         []byte(\"2006-01-02 15:04:05Z\"),\n\t\t\twantTimestamp: NewTimestamp(parseTime(\"2006-01-02 15:04:05Z\")),\n\t\t}, {\n\t\t\tdescription:   \"2006-01-02 15:04:05-07:00\",\n\t\t\tvalue:         \"2006-01-02 15:04:05-07:00\",\n\t\t\twantTimestamp: NewTimestamp(parseTime(\"2006-01-02 15:04:05-07:00\")),\n\t\t}, {\n\t\t\tdescription:   \"2006-01-02 15:04:05.9999\",\n\t\t\tvalue:         []byte(\"2006-01-02 15:04:05.9999\"),\n\t\t\twantTimestamp: NewTimestamp(parseTime(\"2006-01-02 15:04:05.9999\")),\n\t\t}, {\n\t\t\tdescription:   \"int64 seconds\",\n\t\t\tvalue:         int64(123456),\n\t\t\twantTimestamp: NewTimestamp(time.Unix(123456, 0)),\n\t\t}, {\n\t\t\tdescription:   \"int64 milliseconds\",\n\t\t\tvalue:         int64(1e12 + 1),\n\t\t\twantTimestamp: NewTimestamp(time.Unix(0, int64(1e12+1)*int64(time.Millisecond))),\n\t\t}}\n\n\t\tfor _, tt := range tests {\n\t\t\ttt := tt\n\t\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tvar gotTimestamp Timestamp\n\t\t\t\terr := gotTimestamp.Scan(tt.value)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t\t}\n\t\t\t\tif diff := testutil.Diff(gotTimestamp, tt.wantTimestamp); diff != \"\" {\n\t\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestUUIDField(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\ttbl := NewTableStruct(\"\", \"tbl\", \"\")\n\t\tf1 := NewUUIDField(\"field\", tbl).As(\"f\")\n\t\tif diff := testutil.Diff(f1.GetAlias(), \"f\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tfield := NewUUIDField(\"field\", NewTableStruct(\"\", \"tbl\", \"\"))\n\ttests := []TestTable{{\n\t\tdescription: \"IsNull\",\n\t\titem:        field.IsNull(),\n\t\twantQuery:   \"tbl.field IS NULL\",\n\t}, {\n\t\tdescription: \"IsNotNull\",\n\t\titem:        field.IsNotNull(),\n\t\twantQuery:   \"tbl.field IS NOT NULL\",\n\t}, {\n\t\tdescription: \"Asc NullsLast\",\n\t\titem:        field.Asc().NullsLast(),\n\t\twantQuery:   \"tbl.field ASC NULLS LAST\",\n\t}, {\n\t\tdescription: \"Desc NullsFirst\",\n\t\titem:        field.Desc().NullsFirst(),\n\t\twantQuery:   \"tbl.field DESC NULLS FIRST\",\n\t}, {\n\t\tdescription: \"In\",\n\t\titem:        field.In(RowValue{1, 2, 3}),\n\t\twantQuery:   \"tbl.field IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"NotIn\",\n\t\titem:        field.NotIn(RowValue{1, 2, 3}),\n\t\twantQuery:   \"tbl.field NOT IN (?, ?, ?)\", wantArgs: []any{1, 2, 3},\n\t}, {\n\t\tdescription: \"Eq\",\n\t\titem:        field.Eq(field),\n\t\twantQuery:   \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Ne\",\n\t\titem:        field.Ne(field),\n\t\twantQuery:   \"tbl.field <> tbl.field\",\n\t}, {\n\t\tdescription: \"EqUUID\",\n\t\titem: func() any {\n\t\t\tid, err := uuid.Parse(\"ffffffff-ffff-ffff-ffff-ffffffffffff\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\treturn field.EqUUID(id)\n\t\t}(),\n\t\twantQuery: \"tbl.field = ?\",\n\t\twantArgs:  []any{[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},\n\t}, {\n\t\tdescription: \"NeUUID\",\n\t\titem: func() any {\n\t\t\tid, err := uuid.Parse(\"ffffffff-ffff-ffff-ffff-ffffffffffff\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\treturn field.NeUUID(id)\n\t\t}(),\n\t\twantQuery: \"tbl.field <> ?\",\n\t\twantArgs:  []any{[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},\n\t}, {\n\t\tdescription: \"Eq id\",\n\t\titem: func() any {\n\t\t\tid, err := uuid.Parse(\"ffffffff-ffff-ffff-ffff-ffffffffffff\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\treturn field.Eq(id)\n\t\t}(),\n\t\twantQuery: \"tbl.field = ?\",\n\t\twantArgs:  []any{\"ffffffff-ffff-ffff-ffff-ffffffffffff\"},\n\t}, {\n\t\tdescription: \"Ne id\",\n\t\titem: func() any {\n\t\t\tid, err := uuid.Parse(\"ffffffff-ffff-ffff-ffff-ffffffffffff\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\treturn field.Ne(id)\n\t\t}(),\n\t\twantQuery: \"tbl.field <> ?\",\n\t\twantArgs:  []any{\"ffffffff-ffff-ffff-ffff-ffffffffffff\"},\n\t}, {\n\t\tdescription: \"Set\",\n\t\titem: func() any {\n\t\t\tid, err := uuid.Parse(\"ffffffff-ffff-ffff-ffff-ffffffffffff\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\treturn field.Set(id)\n\t\t}(),\n\t\twantQuery: \"field = ?\",\n\t\twantArgs:  []any{\"ffffffff-ffff-ffff-ffff-ffffffffffff\"},\n\t}, {\n\t\tdescription: \"SetUUID\",\n\t\titem: func() any {\n\t\t\tid, err := uuid.Parse(\"ffffffff-ffff-ffff-ffff-ffffffffffff\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\treturn field.SetUUID(id)\n\t\t}(),\n\t\twantQuery: \"field = ?\",\n\t\twantArgs:  []any{[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},\n\t}, {\n\t\tdescription: \"Setf\",\n\t\titem:        field.Setf(\"VALUES({})\", field.WithPrefix(\"\")),\n\t\twantQuery:   \"field = VALUES(field)\",\n\t}, {\n\t\tdescription: \"Set EXCLUDED\",\n\t\titem:        field.Set(field.WithPrefix(\"EXCLUDED\")),\n\t\twantQuery:   \"field = EXCLUDED.field\",\n\t}, {\n\t\tdescription: \"Set self\",\n\t\titem:        field.Set(field), dialect: DialectMySQL,\n\t\twantQuery: \"tbl.field = tbl.field\",\n\t}, {\n\t\tdescription: \"Set with alias\",\n\t\titem:        field.Set(field.WithPrefix(\"new\")),\n\t\twantQuery:   \"field = new.field\",\n\t}, {\n\t\tdescription: \"Set id\",\n\t\titem: func() any {\n\t\t\tid, err := uuid.Parse(\"ffffffff-ffff-ffff-ffff-ffffffffffff\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\treturn field.Set(id)\n\t\t}(),\n\t\twantQuery: \"field = ?\",\n\t\twantArgs:  []any{\"ffffffff-ffff-ffff-ffff-ffffffffffff\"},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\ntype DummyTable struct{}\n\nvar _ Table = (*DummyTable)(nil)\n\nfunc (t DummyTable) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn nil\n}\n\nfunc (t DummyTable) GetAlias() string { return \"\" }\n\nfunc (t DummyTable) IsTable() {}\n\nfunc TestNew(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\ttype USER struct {\n\t\t\tTableStruct\n\t\t\tUSER_ID NumberField\n\t\t\tNAME    StringField\n\t\t\tEMAIL   StringField\n\t\t}\n\t\tu := New[USER](\"u\")\n\t\tTestTable{\n\t\t\titem:      Queryf(\"SELECT {} FROM {} AS {}\", Fields{u.USER_ID, u.NAME, u.EMAIL}, u, Expr(u.GetAlias())),\n\t\t\twantQuery: \"SELECT u.user_id, u.name, u.email FROM user AS u\",\n\t\t}.assert(t)\n\t})\n\n\tt.Run(\"name\", func(t *testing.T) {\n\t\ttype USER struct {\n\t\t\tTableStruct `sq:\"User\"`\n\t\t\tUSER_ID     NumberField `sq:\"UserId\"`\n\t\t\tNAME        StringField `sq:\"Name\"`\n\t\t\tEMAIL       StringField `sq:\"Email\"`\n\t\t\tprivate     int\n\t\t}\n\t\tu := New[USER](\"u\")\n\t\tTestTable{\n\t\t\titem:      Queryf(\"SELECT {} FROM {} AS {}\", Fields{u.USER_ID, u.NAME, u.EMAIL}, u, Expr(u.GetAlias())),\n\t\t\twantQuery: `SELECT u.\"UserId\", u.\"Name\", u.\"Email\" FROM \"User\" AS u`,\n\t\t}.assert(t)\n\t})\n\n\tt.Run(\"schema\", func(t *testing.T) {\n\t\ttype USER struct {\n\t\t\tTableStruct `sq:\"public.user\"`\n\t\t\tUSER_ID     NumberField\n\t\t\tNAME        StringField\n\t\t\tEMAIL       StringField\n\t\t\tPublic      int\n\t\t}\n\t\tu := New[USER](\"u\")\n\t\tTestTable{\n\t\t\titem:      Queryf(\"SELECT {} FROM {} AS {}\", Fields{u.USER_ID, u.NAME, u.EMAIL}, u, Expr(u.GetAlias())),\n\t\t\twantQuery: \"SELECT u.user_id, u.name, u.email FROM public.user AS u\",\n\t\t}.assert(t)\n\t})\n\n\tt.Run(\"first field not a struct\", func(t *testing.T) {\n\t\ttbl := New[tmptable](\"\")\n\t\tif diff := testutil.Diff(tbl, tmptable(\"\")); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"struct has no fields\", func(t *testing.T) {\n\t\ttbl := New[DummyTable](\"\")\n\t\tif diff := testutil.Diff(tbl, DummyTable{}); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"first field is unexported\", func(t *testing.T) {\n\t\ttbl := New[Expression](\"\")\n\t\tif diff := testutil.Diff(tbl, Expression{}); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"first field is not TableStruct\", func(t *testing.T) {\n\t\ttbl := New[struct{ DummyTable }](\"\")\n\t\tif diff := testutil.Diff(tbl, struct{ DummyTable }{}); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "fmt.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode\"\n)\n\n// Writef is a fmt.Sprintf-style function that will write a format string and\n// values slice into an Output. The only recognized placeholder is '{}'.\n// Placeholders can be anonymous (e.g. {}), ordinal (e.g. {1}, {2}, {3}) or\n// named (e.g. {name}, {email}, {age}).\n//\n// - Anonymous placeholders refer to successive values in the values slice.\n// Anonymous placeholders are treated like a series of incrementing ordinal\n// placeholders.\n//\n// - Ordinal placeholders refer to a specific value in the values slice using\n// 1-based indexing.\n//\n// - Named placeholders refer to their corresponding sql.NamedArg value in the\n// values slice. If there are multiple sql.NamedArg values with the same name,\n// the last one wins.\n//\n// If a value is an SQLWriter, its WriteSQL method will be called. Else if a\n// value is a slice, it will undergo slice expansion\n// (https://bokwoon.neocities.org/sq.html#value-expansion). Otherwise, the\n// value is added to the query args slice.\nfunc Writef(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, format string, values []any) error {\n\treturn writef(ctx, dialect, buf, args, params, format, values, nil, nil)\n}\n\nfunc writef(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, format string, values []any, runningValuesIndex *int, ordinalIndex map[int]int) error {\n\t// optimized case when the format string does not contain any '{}'\n\t// placeholders\n\tif i := strings.IndexByte(format, '{'); i < 0 {\n\t\tbuf.WriteString(format)\n\t\treturn nil\n\t}\n\n\t// namedIndex tracks the indexes of the namedArgs that are inside the\n\t// values slice\n\tnamedIndex := make(map[string]int)\n\tfor i, value := range values {\n\t\tvar name string\n\t\tswitch arg := value.(type) {\n\t\tcase sql.NamedArg:\n\t\t\tname = arg.Name\n\t\tcase Parameter:\n\t\t\tname = arg.Name\n\t\tcase ArrayParameter:\n\t\t\tname = arg.Name\n\t\tcase BinaryParameter:\n\t\t\tname = arg.Name\n\t\tcase BooleanParameter:\n\t\t\tname = arg.Name\n\t\tcase EnumParameter:\n\t\t\tname = arg.Name\n\t\tcase JSONParameter:\n\t\t\tname = arg.Name\n\t\tcase NumberParameter:\n\t\t\tname = arg.Name\n\t\tcase StringParameter:\n\t\t\tname = arg.Name\n\t\tcase TimeParameter:\n\t\t\tname = arg.Name\n\t\tcase UUIDParameter:\n\t\t\tname = arg.Name\n\t\t}\n\t\tif name != \"\" {\n\t\t\tif _, ok := namedIndex[name]; ok {\n\t\t\t\treturn fmt.Errorf(\"named parameter {%s} provided more than once\", name)\n\t\t\t}\n\t\t\tnamedIndex[name] = i\n\t\t}\n\t}\n\tbuf.Grow(len(format))\n\tif runningValuesIndex == nil {\n\t\tn := 0\n\t\trunningValuesIndex = &n\n\t}\n\t// ordinalIndex tracks the index of the ordinals that have already been\n\t// written into the args slice\n\tif ordinalIndex == nil {\n\t\tordinalIndex = make(map[int]int)\n\t}\n\n\t// jump to each '{' character in the format string\n\tfor i := strings.IndexByte(format, '{'); i >= 0; i = strings.IndexByte(format, '{') {\n\t\t// Unescape '{{' to '{'\n\t\tif i+1 <= len(format) && format[i+1] == '{' {\n\t\t\tbuf.WriteString(format[:i])\n\t\t\tbuf.WriteByte('{')\n\t\t\tformat = format[i+2:]\n\t\t\tcontinue\n\t\t}\n\t\tbuf.WriteString(format[:i])\n\t\tformat = format[i:]\n\n\t\t// If we can't find the terminating '}' return an error\n\t\tj := strings.IndexByte(format, '}')\n\t\tif j < 0 {\n\t\t\treturn fmt.Errorf(\"no '}' found\")\n\t\t}\n\n\t\tparamName := format[1:j]\n\t\tformat = format[j+1:]\n\t\tfor _, char := range paramName {\n\t\t\tif char != '_' && !unicode.IsLetter(char) && !unicode.IsDigit(char) {\n\t\t\t\treturn fmt.Errorf(\"%q is not a valid param name (only letters, digits and '_' are allowed)\", paramName)\n\t\t\t}\n\t\t}\n\n\t\t// is it an anonymous placeholder? e.g. {}\n\t\tif paramName == \"\" {\n\t\t\tif *runningValuesIndex >= len(values) {\n\t\t\t\treturn fmt.Errorf(\"too few values passed in to Writef, expected more than %d\", runningValuesIndex)\n\t\t\t}\n\t\t\tvalue := values[*runningValuesIndex]\n\t\t\t*runningValuesIndex++\n\t\t\terr := WriteValue(ctx, dialect, buf, args, params, value)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// is it an ordinal placeholder? e.g. {1}, {2}, {3}\n\t\tordinal, err := strconv.Atoi(paramName)\n\t\tif err == nil {\n\t\t\terr = writeOrdinalValue(ctx, dialect, buf, args, params, values, ordinal, ordinalIndex)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// is it a named placeholder? e.g. {name}, {age}, {email}\n\t\tindex, ok := namedIndex[paramName]\n\t\tif !ok {\n\t\t\tavailableParams := make([]string, 0, len(namedIndex))\n\t\t\tfor name := range namedIndex {\n\t\t\t\tavailableParams = append(availableParams, name)\n\t\t\t}\n\t\t\tsort.Strings(availableParams)\n\t\t\treturn fmt.Errorf(\"named parameter {%s} not provided (available params: %s)\", paramName, strings.Join(availableParams, \", \"))\n\t\t}\n\t\tvalue := values[index]\n\t\terr = WriteValue(ctx, dialect, buf, args, params, value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tbuf.WriteString(format)\n\treturn nil\n}\n\n// WriteValue is the equivalent of Writef but for writing a single value into\n// the Output.\nfunc WriteValue(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, value any) error {\n\tif namedArg, ok := value.(sql.NamedArg); ok {\n\t\treturn writeNamedArg(ctx, dialect, buf, args, params, namedArg)\n\t}\n\tif w, ok := value.(SQLWriter); ok {\n\t\treturn w.WriteSQL(ctx, dialect, buf, args, params)\n\t}\n\tif isExpandableSlice(value) {\n\t\treturn expandSlice(ctx, dialect, buf, args, params, value)\n\t}\n\tvalue, err := preprocessValue(dialect, value)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*args = append(*args, value)\n\tindex := len(*args) - 1\n\tswitch dialect {\n\tcase DialectPostgres, DialectSQLite:\n\t\tbuf.WriteString(\"$\" + strconv.Itoa(index+1))\n\tcase DialectSQLServer:\n\t\tbuf.WriteString(\"@p\" + strconv.Itoa(index+1))\n\tdefault:\n\t\tbuf.WriteString(\"?\")\n\t}\n\treturn nil\n}\n\n// QuoteIdentifier quotes an identifier if necessary using dialect-specific\n// quoting rules.\nfunc QuoteIdentifier(dialect string, identifier string) string {\n\tvar needsQuoting bool\n\tswitch identifier {\n\tcase \"\":\n\t\tneedsQuoting = true\n\tcase \"EXCLUDED\", \"INSERTED\", \"DELETED\", \"NEW\", \"OLD\":\n\t\tneedsQuoting = false\n\tdefault:\n\t\tfor i, char := range identifier {\n\t\t\tif i == 0 && (char >= '0' && char <= '9') {\n\t\t\t\t// first character cannot be a number\n\t\t\t\tneedsQuoting = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif char == '_' || (char >= '0' && char <= '9') || (char >= 'a' && char <= 'z') {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// If there are capital letters, the identifier is quoted to preserve\n\t\t\t// capitalization information (because databases treat capital letters\n\t\t\t// differently based on their dialect or configuration).\n\t\t\t// If the character is anything else, we also quote. In general there\n\t\t\t// may be some special characters that are allowed in unquoted\n\t\t\t// identifiers (e.g. '$'), but different databases allow different\n\t\t\t// things. We only recognize _a-z0-9 as the true standard.\n\t\t\tneedsQuoting = true\n\t\t\tbreak\n\t\t}\n\t\tif !needsQuoting && dialect != \"\" {\n\t\t\tswitch dialect {\n\t\t\tcase DialectSQLite:\n\t\t\t\t_, needsQuoting = sqliteKeywords[strings.ToLower(identifier)]\n\t\t\tcase DialectPostgres:\n\t\t\t\t_, needsQuoting = postgresKeywords[strings.ToLower(identifier)]\n\t\t\tcase DialectMySQL:\n\t\t\t\t_, needsQuoting = mysqlKeywords[strings.ToLower(identifier)]\n\t\t\tcase DialectSQLServer:\n\t\t\t\t_, needsQuoting = sqlserverKeywords[strings.ToLower(identifier)]\n\t\t\t}\n\t\t}\n\t}\n\tif !needsQuoting {\n\t\treturn identifier\n\t}\n\tswitch dialect {\n\tcase DialectMySQL:\n\t\treturn \"`\" + EscapeQuote(identifier, '`') + \"`\"\n\tcase DialectSQLServer:\n\t\treturn \"[\" + EscapeQuote(identifier, ']') + \"]\"\n\tdefault:\n\t\treturn `\"` + EscapeQuote(identifier, '\"') + `\"`\n\t}\n}\n\n// EscapeQuote will escape the relevant quote in a string by doubling up on it\n// (as per SQL rules).\nfunc EscapeQuote(str string, quote byte) string {\n\ti := strings.IndexByte(str, quote)\n\tif i < 0 {\n\t\treturn str\n\t}\n\tvar b strings.Builder\n\tb.Grow(len(str) + strings.Count(str, string(quote)))\n\tfor i >= 0 {\n\t\tb.WriteString(str[:i])\n\t\tb.WriteByte(quote)\n\t\tb.WriteByte(quote)\n\t\tif len(str[i:]) > 2 && str[i] == quote && str[i+1] == quote {\n\t\t\tstr = str[i+2:]\n\t\t} else {\n\t\t\tstr = str[i+1:]\n\t\t}\n\t\ti = strings.IndexByte(str, quote)\n\t}\n\tb.WriteString(str)\n\treturn b.String()\n}\n\n// Sprintf will interpolate SQL args into a query string containing prepared\n// statement parameters. It returns an error if an argument cannot be properly\n// represented in SQL. This function may be vulnerable to SQL injection and\n// should be used for logging purposes only.\nfunc Sprintf(dialect string, query string, args []any) (string, error) {\n\tif len(args) == 0 {\n\t\treturn query, nil\n\t}\n\tbuf := bufpool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer bufpool.Put(buf)\n\tbuf.Grow(len(query))\n\tnamedIndices := make(map[string]int)\n\tfor i, arg := range args {\n\t\tswitch arg := arg.(type) {\n\t\tcase sql.NamedArg:\n\t\t\tnamedIndices[arg.Name] = i\n\t\t}\n\t}\n\trunningArgsIndex := 0\n\tmustWriteCharAt := -1\n\tinsideStringOrIdentifier := false\n\tvar openingQuote rune\n\tvar paramName []rune\n\tfor i, char := range query {\n\t\t// do we unconditionally write in the current char?\n\t\tif mustWriteCharAt == i {\n\t\t\tbuf.WriteRune(char)\n\t\t\tcontinue\n\t\t}\n\t\t// are we currently inside a string or identifier?\n\t\tif insideStringOrIdentifier {\n\t\t\tbuf.WriteRune(char)\n\t\t\tswitch openingQuote {\n\t\t\tcase '\\'', '\"', '`':\n\t\t\t\t// does the current char terminate the current string or identifier?\n\t\t\t\tif char == openingQuote {\n\t\t\t\t\t// is the next char the same as the current char, which\n\t\t\t\t\t// escapes it and prevents it from terminating the current\n\t\t\t\t\t// string or identifier?\n\t\t\t\t\tif i+1 < len(query) && rune(query[i+1]) == openingQuote {\n\t\t\t\t\t\tmustWriteCharAt = i + 1\n\t\t\t\t\t} else {\n\t\t\t\t\t\tinsideStringOrIdentifier = false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase '[':\n\t\t\t\t// does the current char terminate the current string or identifier?\n\t\t\t\tif char == ']' {\n\t\t\t\t\t// is the next char the same as the current char, which\n\t\t\t\t\t// escapes it and prevents it from terminating the current\n\t\t\t\t\t// string or identifier?\n\t\t\t\t\tif i+1 < len(query) && query[i+1] == ']' {\n\t\t\t\t\t\tmustWriteCharAt = i + 1\n\t\t\t\t\t} else {\n\t\t\t\t\t\tinsideStringOrIdentifier = false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\t// does the current char mark the start of a new string or identifier?\n\t\tif char == '\\'' || char == '\"' || (char == '`' && dialect == DialectMySQL) || (char == '[' && dialect == DialectSQLServer) {\n\t\t\tinsideStringOrIdentifier = true\n\t\t\topeningQuote = char\n\t\t\tbuf.WriteRune(char)\n\t\t\tcontinue\n\t\t}\n\t\t// are we currently inside a parameter name?\n\t\tif len(paramName) > 0 {\n\t\t\t// does the current char terminate the current parameter name?\n\t\t\tif char != '_' && !unicode.IsLetter(char) && !unicode.IsDigit(char) {\n\t\t\t\tparamValue, err := lookupParam(dialect, args, paramName, namedIndices, runningArgsIndex)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn buf.String(), err\n\t\t\t\t}\n\t\t\t\tbuf.WriteString(paramValue)\n\t\t\t\tbuf.WriteRune(char)\n\t\t\t\tif len(paramName) == 1 && paramName[0] == '?' {\n\t\t\t\t\trunningArgsIndex++\n\t\t\t\t}\n\t\t\t\tparamName = paramName[:0]\n\t\t\t} else {\n\t\t\t\tparamName = append(paramName, char)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\t// does the current char mark the start of a new parameter name?\n\t\tif (char == '$' && (dialect == DialectSQLite || dialect == DialectPostgres)) ||\n\t\t\t(char == ':' && dialect == DialectSQLite) ||\n\t\t\t(char == '@' && (dialect == DialectSQLite || dialect == DialectSQLServer)) {\n\t\t\tparamName = append(paramName, char)\n\t\t\tcontinue\n\t\t}\n\t\t// is the current char the anonymous '?' parameter?\n\t\tif char == '?' && dialect != DialectPostgres {\n\t\t\t// for sqlite, just because we encounter a '?' doesn't mean it\n\t\t\t// is an anonymous param. sqlite also supports using '?' for\n\t\t\t// ordinal params (e.g. ?1, ?2, ?3) or named params (?foo,\n\t\t\t// ?bar, ?baz). Hence we treat it as an ordinal/named param\n\t\t\t// first, and handle the edge case later when it isn't.\n\t\t\tif dialect == DialectSQLite {\n\t\t\t\tparamName = append(paramName, char)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif runningArgsIndex >= len(args) {\n\t\t\t\treturn buf.String(), fmt.Errorf(\"too few args provided, expected more than %d\", runningArgsIndex+1)\n\t\t\t}\n\t\t\tparamValue, err := Sprint(dialect, args[runningArgsIndex])\n\t\t\tif err != nil {\n\t\t\t\treturn buf.String(), err\n\t\t\t}\n\t\t\tbuf.WriteString(paramValue)\n\t\t\trunningArgsIndex++\n\t\t\tcontinue\n\t\t}\n\t\t// if all the above questions answer false, we just write the current\n\t\t// char in and continue\n\t\tbuf.WriteRune(char)\n\t}\n\t// flush the paramName buffer (to handle edge case where the query ends with a parameter name)\n\tif len(paramName) > 0 {\n\t\tparamValue, err := lookupParam(dialect, args, paramName, namedIndices, runningArgsIndex)\n\t\tif err != nil {\n\t\t\treturn buf.String(), err\n\t\t}\n\t\tbuf.WriteString(paramValue)\n\t}\n\tif insideStringOrIdentifier {\n\t\treturn buf.String(), fmt.Errorf(\"unclosed string or identifier\")\n\t}\n\treturn buf.String(), nil\n}\n\n// Sprint is the equivalent of Sprintf but for converting a single value into\n// its SQL representation.\nfunc Sprint(dialect string, v any) (string, error) {\n\tconst (\n\t\ttimestamp             = \"2006-01-02 15:04:05\"\n\t\ttimestampWithTimezone = \"2006-01-02 15:04:05.9999999-07:00\"\n\t)\n\tswitch v := v.(type) {\n\tcase nil:\n\t\treturn \"NULL\", nil\n\tcase bool:\n\t\tif v {\n\t\t\tif dialect == DialectSQLServer {\n\t\t\t\treturn \"1\", nil\n\t\t\t}\n\t\t\treturn \"TRUE\", nil\n\t\t}\n\t\tif dialect == DialectSQLServer {\n\t\t\treturn \"0\", nil\n\t\t}\n\t\treturn \"FALSE\", nil\n\tcase []byte:\n\t\tswitch dialect {\n\t\tcase DialectPostgres:\n\t\t\t// https://www.postgresql.org/docs/current/datatype-binary.html\n\t\t\t// (see 8.4.1. bytea Hex Format)\n\t\t\treturn `'\\x` + hex.EncodeToString(v) + `'`, nil\n\t\tcase DialectSQLServer:\n\t\t\treturn `0x` + hex.EncodeToString(v), nil\n\t\tdefault:\n\t\t\treturn `x'` + hex.EncodeToString(v) + `'`, nil\n\t\t}\n\tcase string:\n\t\tstr := v\n\t\ti := strings.IndexAny(str, \"\\r\\n\")\n\t\tif i < 0 {\n\t\t\treturn `'` + strings.ReplaceAll(str, `'`, `''`) + `'`, nil\n\t\t}\n\t\tvar b strings.Builder\n\t\tif dialect == DialectMySQL || dialect == DialectSQLServer {\n\t\t\tb.WriteString(\"CONCAT(\")\n\t\t}\n\t\tfor i >= 0 {\n\t\t\tif str[:i] != \"\" {\n\t\t\t\tb.WriteString(`'` + strings.ReplaceAll(str[:i], `'`, `''`) + `'`)\n\t\t\t\tif dialect == DialectMySQL || dialect == DialectSQLServer {\n\t\t\t\t\tb.WriteString(\", \")\n\t\t\t\t} else {\n\t\t\t\t\tb.WriteString(\" || \")\n\t\t\t\t}\n\t\t\t}\n\t\t\tswitch str[i] {\n\t\t\tcase '\\r':\n\t\t\t\tif dialect == DialectPostgres {\n\t\t\t\t\tb.WriteString(\"CHR(13)\")\n\t\t\t\t} else {\n\t\t\t\t\tb.WriteString(\"CHAR(13)\")\n\t\t\t\t}\n\t\t\tcase '\\n':\n\t\t\t\tif dialect == DialectPostgres {\n\t\t\t\t\tb.WriteString(\"CHR(10)\")\n\t\t\t\t} else {\n\t\t\t\t\tb.WriteString(\"CHAR(10)\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif str[i+1:] != \"\" {\n\t\t\t\tif dialect == DialectMySQL || dialect == DialectSQLServer {\n\t\t\t\t\tb.WriteString(\", \")\n\t\t\t\t} else {\n\t\t\t\t\tb.WriteString(\" || \")\n\t\t\t\t}\n\t\t\t}\n\t\t\tstr = str[i+1:]\n\t\t\ti = strings.IndexAny(str, \"\\r\\n\")\n\t\t}\n\t\tif str != \"\" {\n\t\t\tb.WriteString(`'` + strings.ReplaceAll(str, `'`, `''`) + `'`)\n\t\t}\n\t\tif dialect == DialectMySQL || dialect == DialectSQLServer {\n\t\t\tb.WriteString(\")\")\n\t\t}\n\t\treturn b.String(), nil\n\tcase time.Time:\n\t\tif dialect == DialectPostgres || dialect == DialectSQLServer {\n\t\t\treturn `'` + v.Format(timestampWithTimezone) + `'`, nil\n\t\t}\n\t\treturn `'` + v.UTC().Format(timestamp) + `'`, nil\n\tcase int:\n\t\treturn strconv.FormatInt(int64(v), 10), nil\n\tcase int8:\n\t\treturn strconv.FormatInt(int64(v), 10), nil\n\tcase int16:\n\t\treturn strconv.FormatInt(int64(v), 10), nil\n\tcase int32:\n\t\treturn strconv.FormatInt(int64(v), 10), nil\n\tcase int64:\n\t\treturn strconv.FormatInt(v, 10), nil\n\tcase uint:\n\t\treturn strconv.FormatUint(uint64(v), 10), nil\n\tcase uint8:\n\t\treturn strconv.FormatUint(uint64(v), 10), nil\n\tcase uint16:\n\t\treturn strconv.FormatUint(uint64(v), 10), nil\n\tcase uint32:\n\t\treturn strconv.FormatUint(uint64(v), 10), nil\n\tcase uint64:\n\t\treturn strconv.FormatUint(v, 10), nil\n\tcase float32:\n\t\treturn strconv.FormatFloat(float64(v), 'g', -1, 64), nil\n\tcase float64:\n\t\treturn strconv.FormatFloat(v, 'g', -1, 64), nil\n\tcase sql.NamedArg:\n\t\treturn Sprint(dialect, v.Value)\n\tcase sql.NullBool:\n\t\tif !v.Valid {\n\t\t\treturn \"NULL\", nil\n\t\t}\n\t\tif v.Bool {\n\t\t\tif dialect == DialectSQLServer {\n\t\t\t\treturn \"1\", nil\n\t\t\t}\n\t\t\treturn \"TRUE\", nil\n\t\t}\n\t\tif dialect == DialectSQLServer {\n\t\t\treturn \"0\", nil\n\t\t}\n\t\treturn \"FALSE\", nil\n\tcase sql.NullFloat64:\n\t\tif !v.Valid {\n\t\t\treturn \"NULL\", nil\n\t\t}\n\t\treturn strconv.FormatFloat(v.Float64, 'g', -1, 64), nil\n\tcase sql.NullInt64:\n\t\tif !v.Valid {\n\t\t\treturn \"NULL\", nil\n\t\t}\n\t\treturn strconv.FormatInt(v.Int64, 10), nil\n\tcase sql.NullInt32:\n\t\tif !v.Valid {\n\t\t\treturn \"NULL\", nil\n\t\t}\n\t\treturn strconv.FormatInt(int64(v.Int32), 10), nil\n\tcase sql.NullString:\n\t\tif !v.Valid {\n\t\t\treturn \"NULL\", nil\n\t\t}\n\t\treturn Sprint(dialect, v.String)\n\tcase sql.NullTime:\n\t\tif !v.Valid {\n\t\t\treturn \"NULL\", nil\n\t\t}\n\t\tif dialect == DialectPostgres || dialect == DialectSQLServer {\n\t\t\treturn `'` + v.Time.Format(timestampWithTimezone) + `'`, nil\n\t\t}\n\t\treturn `'` + v.Time.UTC().Format(timestamp) + `'`, nil\n\tcase driver.Valuer:\n\t\tvv, err := v.Value()\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"error when calling Value(): %w\", err)\n\t\t}\n\t\tswitch vv.(type) {\n\t\tcase int64, float64, bool, []byte, string, time.Time, nil:\n\t\t\treturn Sprint(dialect, vv)\n\t\tdefault:\n\t\t\treturn \"\", fmt.Errorf(\"invalid driver.Value type %T (must be one of int64, float64, bool, []byte, string, time.Time, nil)\", vv)\n\t\t}\n\t}\n\trv := reflect.ValueOf(v)\n\tif rv.Kind() == reflect.Pointer {\n\t\trv = rv.Elem()\n\t\tif !rv.IsValid() {\n\t\t\treturn \"NULL\", nil\n\t\t}\n\t}\n\tswitch v := rv.Interface().(type) {\n\tcase bool, []byte, string, time.Time, int, int8, int16, int32, int64, uint,\n\t\tuint8, uint16, uint32, uint64, float32, float64, sql.NamedArg,\n\t\tsql.NullBool, sql.NullFloat64, sql.NullInt64, sql.NullInt32,\n\t\tsql.NullString, sql.NullTime, driver.Valuer:\n\t\treturn Sprint(dialect, v)\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"%T has no SQL representation\", v)\n\t}\n}\n\n// isExpandableSlice checks if a value is an expandable slice.\nfunc isExpandableSlice(value any) bool {\n\t// treat byte slices as a special case that we never want to expand\n\tif _, ok := value.([]byte); ok {\n\t\treturn false\n\t}\n\tvalueType := reflect.TypeOf(value)\n\tif valueType == nil {\n\t\treturn false\n\t}\n\treturn valueType.Kind() == reflect.Slice\n}\n\n// expandSlice expands a slice value into Output. Make sure the value is an\n// expandable slice first by checking it with isExpandableSlice().\nfunc expandSlice(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, value any) error {\n\tslice := reflect.ValueOf(value)\n\tvar err error\n\tfor i := 0; i < slice.Len(); i++ {\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\targ := slice.Index(i).Interface()\n\t\tif v, ok := arg.(SQLWriter); ok {\n\t\t\terr = v.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tswitch dialect {\n\t\tcase DialectPostgres, DialectSQLite:\n\t\t\tbuf.WriteString(\"$\" + strconv.Itoa(len(*args)+1))\n\t\tcase DialectSQLServer:\n\t\t\tbuf.WriteString(\"@p\" + strconv.Itoa(len(*args)+1))\n\t\tdefault:\n\t\t\tbuf.WriteString(\"?\")\n\t\t}\n\t\targ, err = preprocessValue(dialect, arg)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*args = append(*args, arg)\n\t}\n\treturn nil\n}\n\n// writeNamedArg writes an sql.NamedArg into the Output.\nfunc writeNamedArg(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, namedArg sql.NamedArg) error {\n\tif w, ok := namedArg.Value.(SQLWriter); ok {\n\t\treturn w.WriteSQL(ctx, dialect, buf, args, params)\n\t}\n\tif isExpandableSlice(namedArg.Value) {\n\t\treturn expandSlice(ctx, dialect, buf, args, params, namedArg.Value)\n\t}\n\tvar err error\n\tnamedArg.Value, err = preprocessValue(dialect, namedArg.Value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tparamIndices := params[namedArg.Name]\n\tif len(paramIndices) > 0 {\n\t\tindex := paramIndices[0]\n\t\tswitch dialect {\n\t\tcase DialectSQLite:\n\t\t\t(*args)[index] = namedArg\n\t\t\tbuf.WriteString(\"$\" + namedArg.Name)\n\t\t\treturn nil\n\t\tcase DialectPostgres:\n\t\t\t(*args)[index] = namedArg.Value\n\t\t\tbuf.WriteString(\"$\" + strconv.Itoa(index+1))\n\t\t\treturn nil\n\t\tcase DialectSQLServer:\n\t\t\t(*args)[index] = namedArg\n\t\t\tbuf.WriteString(\"@\" + namedArg.Name)\n\t\t\treturn nil\n\t\tdefault:\n\t\t\tfor _, index := range paramIndices {\n\t\t\t\t(*args)[index] = namedArg.Value\n\t\t\t}\n\t\t}\n\t}\n\tswitch dialect {\n\tcase DialectSQLite:\n\t\t*args = append(*args, namedArg)\n\t\tif params != nil {\n\t\t\tindex := len(*args) - 1\n\t\t\tparams[namedArg.Name] = []int{index}\n\t\t}\n\t\tbuf.WriteString(\"$\" + namedArg.Name)\n\tcase DialectPostgres:\n\t\t*args = append(*args, namedArg.Value)\n\t\tindex := len(*args) - 1\n\t\tif params != nil {\n\t\t\tparams[namedArg.Name] = []int{index}\n\t\t}\n\t\tbuf.WriteString(\"$\" + strconv.Itoa(index+1))\n\tcase DialectSQLServer:\n\t\t*args = append(*args, namedArg)\n\t\tif params != nil {\n\t\t\tindex := len(*args) - 1\n\t\t\tparams[namedArg.Name] = []int{index}\n\t\t}\n\t\tbuf.WriteString(\"@\" + namedArg.Name)\n\tdefault:\n\t\t*args = append(*args, namedArg.Value)\n\t\tif params != nil {\n\t\t\tindex := len(*args) - 1\n\t\t\tparams[namedArg.Name] = append(paramIndices, index)\n\t\t}\n\t\tbuf.WriteString(\"?\")\n\t}\n\treturn nil\n}\n\n// writeOrdinalValue writes an ordinal value into the Output. The\n// ordinalIndices map is there to keep track of which ordinal values we have\n// already appended to args (which we do not want to append again).\nfunc writeOrdinalValue(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, values []any, ordinal int, ordinalIndices map[int]int) error {\n\tindex := ordinal - 1\n\tif index < 0 || index >= len(values) {\n\t\treturn fmt.Errorf(\"ordinal parameter {%d} is out of bounds\", ordinal)\n\t}\n\tvalue := values[index]\n\tif namedArg, ok := value.(sql.NamedArg); ok {\n\t\treturn writeNamedArg(ctx, dialect, buf, args, params, namedArg)\n\t}\n\tif w, ok := value.(SQLWriter); ok {\n\t\treturn w.WriteSQL(ctx, dialect, buf, args, params)\n\t}\n\tif isExpandableSlice(value) {\n\t\treturn expandSlice(ctx, dialect, buf, args, params, value)\n\t}\n\tvar err error\n\tvalue, err = preprocessValue(dialect, value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tswitch dialect {\n\tcase DialectSQLite, DialectPostgres, DialectSQLServer:\n\t\tindex, ok := ordinalIndices[ordinal]\n\t\tif !ok {\n\t\t\t*args = append(*args, value)\n\t\t\tindex = len(*args) - 1\n\t\t\tordinalIndices[ordinal] = index\n\t\t}\n\t\tswitch dialect {\n\t\tcase DialectSQLite, DialectPostgres:\n\t\t\tbuf.WriteString(\"$\" + strconv.Itoa(index+1))\n\t\tcase DialectSQLServer:\n\t\t\tbuf.WriteString(\"@p\" + strconv.Itoa(index+1))\n\t\t}\n\tdefault:\n\t\terr := WriteValue(ctx, dialect, buf, args, params, value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// lookupParam returns the SQL representation of a paramName (inside the args\n// slice).\nfunc lookupParam(dialect string, args []any, paramName []rune, namedIndices map[string]int, runningArgsIndex int) (paramValue string, err error) {\n\tvar maybeNum string\n\tif paramName[0] == '@' && dialect == DialectSQLServer && len(paramName) >= 2 && (paramName[1] == 'p' || paramName[1] == 'P') {\n\t\tmaybeNum = string(paramName[2:])\n\t} else {\n\t\tmaybeNum = string(paramName[1:])\n\t}\n\n\t// is paramName an anonymous parameter?\n\tif maybeNum == \"\" {\n\t\tif paramName[0] != '?' {\n\t\t\treturn \"\", fmt.Errorf(\"parameter name missing\")\n\t\t}\n\t\tparamValue, err = Sprint(dialect, args[runningArgsIndex])\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn paramValue, nil\n\t}\n\n\t// is paramName an ordinal paramater?\n\tordinal, err := strconv.Atoi(maybeNum)\n\tif err == nil {\n\t\tindex := ordinal - 1\n\t\tif index < 0 || index >= len(args) {\n\t\t\treturn \"\", fmt.Errorf(\"args index %d out of bounds\", ordinal)\n\t\t}\n\t\tparamValue, err = Sprint(dialect, args[index])\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn paramValue, nil\n\t}\n\n\t// if we reach here, we know that the paramName is not an ordinal parameter\n\t// i.e. it is a named parameter\n\tif dialect == DialectPostgres || dialect == DialectMySQL {\n\t\treturn \"\", fmt.Errorf(\"%s does not support %s named parameter\", dialect, string(paramName))\n\t}\n\tindex, ok := namedIndices[string(paramName[1:])]\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"named parameter %s not provided\", string(paramName))\n\t}\n\tif index < 0 || index >= len(args) {\n\t\treturn \"\", fmt.Errorf(\"args index %d out of bounds\", ordinal)\n\t}\n\tparamValue, err = Sprint(dialect, args[index])\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn paramValue, nil\n}\n\nfunc quoteTableColumns(dialect string, table Table) string {\n\ttableWithColumns, ok := table.(interface{ GetColumns() []string })\n\tif !ok {\n\t\treturn \"\"\n\t}\n\tcolumns := tableWithColumns.GetColumns()\n\tif len(columns) == 0 {\n\t\treturn \"\"\n\t}\n\tvar b strings.Builder\n\tb.WriteString(\" (\")\n\tfor i, column := range columns {\n\t\tif i > 0 {\n\t\t\tb.WriteString(\", \")\n\t\t}\n\t\tb.WriteString(QuoteIdentifier(dialect, column))\n\t}\n\tb.WriteString(\")\")\n\treturn b.String()\n}\n\n// Params is a shortcut for typing map[string]interface{}.\ntype Params = map[string]any\n\n// Parameter is identical to sql.NamedArg, but implements the Field interface.\ntype Parameter sql.NamedArg\n\nvar _ Field = (*Parameter)(nil)\n\n// Param creates a new Parameter.\nfunc Param(name string, value any) Parameter {\n\treturn Parameter{Name: name, Value: value}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (p Parameter) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn writeNamedArg(ctx, dialect, buf, args, params, sql.NamedArg(p))\n}\n\n// IsField implements the Field interface.\nfunc (p Parameter) IsField() {}\n\n// ArrayParameter is identical to sql.NamedArg, but implements the Array interface.\ntype ArrayParameter sql.NamedArg\n\nvar _ Field = (*ArrayParameter)(nil)\n\n// ArrayParam creates a new ArrayParameter. It wraps the value with\n// ArrayValue().\nfunc ArrayParam(name string, value any) ArrayParameter {\n\treturn ArrayParameter{Name: name, Value: ArrayValue(value)}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (p ArrayParameter) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn writeNamedArg(ctx, dialect, buf, args, params, sql.NamedArg(p))\n}\n\n// IsField implements the Field interface.\nfunc (p ArrayParameter) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (p ArrayParameter) IsArray() {}\n\n// BinaryParameter is identical to sql.NamedArg, but implements the Binary\n// interface.\ntype BinaryParameter sql.NamedArg\n\nvar _ Binary = (*BinaryParameter)(nil)\n\n// BytesParam creates a new BinaryParameter using a []byte value.\nfunc BytesParam(name string, b []byte) BinaryParameter {\n\treturn BinaryParameter{Name: name, Value: b}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (p BinaryParameter) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn writeNamedArg(ctx, dialect, buf, args, params, sql.NamedArg(p))\n}\n\n// IsField implements the Field interface.\nfunc (p BinaryParameter) IsField() {}\n\n// IsBinary implements the Binary interface.\nfunc (p BinaryParameter) IsBinary() {}\n\n// BooleanParameter is identical to sql.NamedArg, but implements the Boolean\n// interface.\ntype BooleanParameter sql.NamedArg\n\nvar _ Boolean = (*BooleanParameter)(nil)\n\n// BoolParam creates a new BooleanParameter from a bool value.\nfunc BoolParam(name string, b bool) BooleanParameter {\n\treturn BooleanParameter{Name: name, Value: b}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (p BooleanParameter) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn writeNamedArg(ctx, dialect, buf, args, params, sql.NamedArg(p))\n}\n\n// IsField implements the Field interface.\nfunc (p BooleanParameter) IsField() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (p BooleanParameter) IsBoolean() {}\n\n// EnumParameter is identical to sql.NamedArg, but implements the Enum\n// interface.\ntype EnumParameter sql.NamedArg\n\nvar _ Field = (*EnumParameter)(nil)\n\n// EnumParam creates a new EnumParameter. It wraps the value with EnumValue().\nfunc EnumParam(name string, value Enumeration) EnumParameter {\n\treturn EnumParameter{Name: name, Value: EnumValue(value)}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (p EnumParameter) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn writeNamedArg(ctx, dialect, buf, args, params, sql.NamedArg(p))\n}\n\n// IsField implements the Field interface.\nfunc (p EnumParameter) IsField() {}\n\n// IsEnum implements the Enum interface.\nfunc (p EnumParameter) IsEnum() {}\n\n// JSONParameter is identical to sql.NamedArg, but implements the JSON\n// interface.\ntype JSONParameter sql.NamedArg\n\nvar _ Field = (*JSONParameter)(nil)\n\n// JSONParam creates a new JSONParameter. It wraps the value with JSONValue().\nfunc JSONParam(name string, value any) JSONParameter {\n\treturn JSONParameter{Name: name, Value: JSONValue(value)}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (p JSONParameter) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn writeNamedArg(ctx, dialect, buf, args, params, sql.NamedArg(p))\n}\n\n// IsField implements the Field interface.\nfunc (p JSONParameter) IsField() {}\n\n// IsJSON implements the JSON interface.\nfunc (p JSONParameter) IsJSON() {}\n\n// NumberParameter is identical to sql.NamedArg, but implements the Number\n// interface.\ntype NumberParameter sql.NamedArg\n\nvar _ Number = (*NumberParameter)(nil)\n\n// IntParam creates a new NumberParameter from an int value.\nfunc IntParam(name string, num int) NumberParameter {\n\treturn NumberParameter{Name: name, Value: num}\n}\n\n// Int64Param creates a new NumberParameter from an int64 value.\nfunc Int64Param(name string, num int64) NumberParameter {\n\treturn NumberParameter{Name: name, Value: num}\n}\n\n// Float64Param creates a new NumberParameter from an float64 value.\nfunc Float64Param(name string, num float64) NumberParameter {\n\treturn NumberParameter{Name: name, Value: num}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (p NumberParameter) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn writeNamedArg(ctx, dialect, buf, args, params, sql.NamedArg(p))\n}\n\n// IsField implements the Field interface.\nfunc (p NumberParameter) IsField() {}\n\n// IsNumber implements the Number interface.\nfunc (p NumberParameter) IsNumber() {}\n\n// StringParameter is identical to sql.NamedArg, but implements the String\n// interface.\ntype StringParameter sql.NamedArg\n\nvar _ String = (*StringParameter)(nil)\n\n// StringParam creates a new StringParameter from a string value.\nfunc StringParam(name string, s string) StringParameter {\n\treturn StringParameter{Name: name, Value: s}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (p StringParameter) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn writeNamedArg(ctx, dialect, buf, args, params, sql.NamedArg(p))\n}\n\n// IsField implements the Field interface.\nfunc (p StringParameter) IsField() {}\n\n// IsString implements the String interface.\nfunc (p StringParameter) IsString() {}\n\n// TimeParameter is identical to sql.NamedArg, but implements the Time\n// interface.\ntype TimeParameter sql.NamedArg\n\nvar _ Time = (*TimeParameter)(nil)\n\n// TimeParam creates a new TimeParameter from a time.Time value.\nfunc TimeParam(name string, t time.Time) TimeParameter {\n\treturn TimeParameter{Name: name, Value: t}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (p TimeParameter) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn writeNamedArg(ctx, dialect, buf, args, params, sql.NamedArg(p))\n}\n\n// IsField implements the Field interface.\nfunc (p TimeParameter) IsField() {}\n\n// IsTime implements the Time interface.\nfunc (p TimeParameter) IsTime() {}\n\n// UUIDParameter is identical to sql.NamedArg, but implements the UUID\n// interface.\ntype UUIDParameter sql.NamedArg\n\nvar _ Field = (*UUIDParameter)(nil)\n\n// UUIDParam creates a new UUIDParameter. It wraps the value with UUIDValue().\nfunc UUIDParam(name string, value any) UUIDParameter {\n\treturn UUIDParameter{Name: name, Value: UUIDValue(value)}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (p UUIDParameter) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn writeNamedArg(ctx, dialect, buf, args, params, sql.NamedArg(p))\n}\n\n// IsField implements the Field interface.\nfunc (p UUIDParameter) IsField() {}\n\n// IsUUID implements the UUID interface.\nfunc (p UUIDParameter) IsUUID() {}\n\n// SQLite keyword reference: https://www.sqlite.org/lang_keywords.html\nvar sqliteKeywords = map[string]struct{}{\n\t\"abort\": {}, \"action\": {}, \"add\": {}, \"after\": {}, \"all\": {}, \"alter\": {},\n\t\"always\": {}, \"analyze\": {}, \"and\": {}, \"as\": {}, \"asc\": {}, \"attach\": {},\n\t\"autoincrement\": {}, \"before\": {}, \"begin\": {}, \"between\": {}, \"by\": {},\n\t\"cascade\": {}, \"case\": {}, \"cast\": {}, \"check\": {}, \"collate\": {}, \"column\": {},\n\t\"commit\": {}, \"conflict\": {}, \"constraint\": {}, \"create\": {}, \"cross\": {},\n\t\"current\": {}, \"current_date\": {}, \"current_time\": {}, \"current_timestamp\": {},\n\t\"database\": {}, \"default\": {}, \"deferrable\": {}, \"deferred\": {}, \"delete\": {},\n\t\"desc\": {}, \"detach\": {}, \"distinct\": {}, \"do\": {}, \"drop\": {}, \"each\": {},\n\t\"else\": {}, \"end\": {}, \"escape\": {}, \"except\": {}, \"exclude\": {}, \"exclusive\": {},\n\t\"exists\": {}, \"explain\": {}, \"fail\": {}, \"filter\": {}, \"first\": {}, \"following\": {},\n\t\"for\": {}, \"foreign\": {}, \"from\": {}, \"full\": {}, \"generated\": {}, \"glob\": {},\n\t\"group\": {}, \"groups\": {}, \"having\": {}, \"if\": {}, \"ignore\": {}, \"immediate\": {},\n\t\"in\": {}, \"index\": {}, \"indexed\": {}, \"initially\": {}, \"inner\": {}, \"insert\": {},\n\t\"instead\": {}, \"intersect\": {}, \"into\": {}, \"is\": {}, \"isnull\": {}, \"join\": {},\n\t\"key\": {}, \"last\": {}, \"left\": {}, \"like\": {}, \"limit\": {}, \"match\": {},\n\t\"materialized\": {}, \"natural\": {}, \"no\": {}, \"not\": {}, \"nothing\": {}, \"notnull\": {},\n\t\"null\": {}, \"nulls\": {}, \"of\": {}, \"offset\": {}, \"on\": {}, \"or\": {}, \"order\": {},\n\t\"others\": {}, \"outer\": {}, \"over\": {}, \"partition\": {}, \"plan\": {}, \"pragma\": {},\n\t\"preceding\": {}, \"primary\": {}, \"query\": {}, \"raise\": {}, \"range\": {},\n\t\"recursive\": {}, \"references\": {}, \"regexp\": {}, \"reindex\": {}, \"release\": {},\n\t\"rename\": {}, \"replace\": {}, \"restrict\": {}, \"returning\": {}, \"right\": {},\n\t\"rollback\": {}, \"row\": {}, \"rows\": {}, \"savepoint\": {}, \"select\": {}, \"set\": {},\n\t\"table\": {}, \"temp\": {}, \"temporary\": {}, \"then\": {}, \"ties\": {}, \"to\": {},\n\t\"transaction\": {}, \"trigger\": {}, \"unbounded\": {}, \"union\": {}, \"unique\": {},\n\t\"update\": {}, \"using\": {}, \"vacuum\": {}, \"values\": {}, \"view\": {}, \"virtual\": {},\n\t\"when\": {}, \"where\": {}, \"window\": {}, \"with\": {}, \"without\": {},\n}\n\n// Postgres keyword reference:\n// https://www.postgresql.org/docs/current/sql-keywords-appendix.html\nvar postgresKeywords = map[string]struct{}{\n\t\"all\": {}, \"analyse\": {}, \"analyze\": {}, \"and\": {}, \"any\": {}, \"array\": {}, \"as\": {},\n\t\"asc\": {}, \"asymmetric\": {}, \"authorization\": {}, \"binary\": {}, \"both\": {},\n\t\"case\": {}, \"cast\": {}, \"check\": {}, \"collate\": {}, \"collation\": {}, \"column\": {},\n\t\"concurrently\": {}, \"constraint\": {}, \"create\": {}, \"cross\": {},\n\t\"current_catalog\": {}, \"current_date\": {}, \"current_role\": {},\n\t\"current_schema\": {}, \"current_time\": {}, \"current_timestamp\": {},\n\t\"current_user\": {}, \"default\": {}, \"deferrable\": {}, \"desc\": {}, \"distinct\": {},\n\t\"do\": {}, \"else\": {}, \"end\": {}, \"except\": {}, \"false\": {}, \"fetch\": {}, \"for\": {},\n\t\"foreign\": {}, \"freeze\": {}, \"from\": {}, \"full\": {}, \"grant\": {}, \"group\": {},\n\t\"having\": {}, \"ilike\": {}, \"in\": {}, \"initially\": {}, \"inner\": {}, \"intersect\": {},\n\t\"into\": {}, \"is\": {}, \"isnull\": {}, \"join\": {}, \"lateral\": {}, \"leading\": {},\n\t\"left\": {}, \"like\": {}, \"limit\": {}, \"localtime\": {}, \"localtimestamp\": {},\n\t\"natural\": {}, \"not\": {}, \"notnull\": {}, \"null\": {}, \"offset\": {}, \"on\": {},\n\t\"only\": {}, \"or\": {}, \"order\": {}, \"outer\": {}, \"overlaps\": {}, \"placing\": {},\n\t\"primary\": {}, \"references\": {}, \"returning\": {}, \"right\": {}, \"select\": {},\n\t\"session_user\": {}, \"similar\": {}, \"some\": {}, \"symmetric\": {}, \"table\": {},\n\t\"tablesample\": {}, \"then\": {}, \"to\": {}, \"trailing\": {}, \"true\": {}, \"union\": {},\n\t\"unique\": {}, \"user\": {}, \"using\": {}, \"variadic\": {}, \"verbose\": {}, \"when\": {},\n\t\"where\": {}, \"window\": {}, \"with\": {},\n}\n\n// MySQL keyword reference:\n// https://dev.mysql.com/doc/refman/8.0/en/keywords.html\nvar mysqlKeywords = map[string]struct{}{\n\t\"accessible\": {}, \"add\": {}, \"all\": {}, \"alter\": {}, \"analyze\": {}, \"and\": {},\n\t\"as\": {}, \"asc\": {}, \"asensitive\": {}, \"before\": {}, \"between\": {}, \"bigint\": {},\n\t\"binary\": {}, \"blob\": {}, \"both\": {}, \"by\": {}, \"call\": {}, \"cascade\": {}, \"case\": {},\n\t\"change\": {}, \"char\": {}, \"character\": {}, \"check\": {}, \"collate\": {}, \"column\": {},\n\t\"condition\": {}, \"constraint\": {}, \"continue\": {}, \"convert\": {}, \"create\": {},\n\t\"cross\": {}, \"cube\": {}, \"cume_dist\": {}, \"current_date\": {}, \"current_time\": {},\n\t\"current_timestamp\": {}, \"current_user\": {}, \"cursor\": {}, \"database\": {},\n\t\"databases\": {}, \"day_hour\": {}, \"day_microsecond\": {}, \"day_minute\": {},\n\t\"day_second\": {}, \"dec\": {}, \"decimal\": {}, \"declare\": {}, \"default\": {},\n\t\"delayed\": {}, \"delete\": {}, \"dense_rank\": {}, \"desc\": {}, \"describe\": {},\n\t\"deterministic\": {}, \"distinct\": {}, \"distinctrow\": {}, \"div\": {}, \"double\": {},\n\t\"drop\": {}, \"dual\": {}, \"each\": {}, \"else\": {}, \"elseif\": {}, \"empty\": {},\n\t\"enclosed\": {}, \"escaped\": {}, \"except\": {}, \"exists\": {}, \"exit\": {}, \"explain\": {},\n\t\"false\": {}, \"fetch\": {}, \"first_value\": {}, \"float\": {}, \"float4\": {}, \"float8\": {},\n\t\"for\": {}, \"force\": {}, \"foreign\": {}, \"from\": {}, \"fulltext\": {}, \"function\": {},\n\t\"generated\": {}, \"get\": {}, \"grant\": {}, \"group\": {}, \"grouping\": {}, \"groups\": {},\n\t\"having\": {}, \"high_priority\": {}, \"hour_microsecond\": {}, \"hour_minute\": {},\n\t\"hour_second\": {}, \"if\": {}, \"ignore\": {}, \"in\": {}, \"index\": {}, \"infile\": {},\n\t\"inner\": {}, \"inout\": {}, \"insensitive\": {}, \"insert\": {}, \"int\": {}, \"int1\": {},\n\t\"int2\": {}, \"int3\": {}, \"int4\": {}, \"int8\": {}, \"integer\": {}, \"intersect\": {},\n\t\"interval\": {}, \"into\": {}, \"io_after_gtids\": {}, \"io_before_gtids\": {}, \"is\": {},\n\t\"iterate\": {}, \"join\": {}, \"json_table\": {}, \"key\": {}, \"keys\": {}, \"kill\": {},\n\t\"lag\": {}, \"last_value\": {}, \"lateral\": {}, \"lead\": {}, \"leading\": {}, \"leave\": {},\n\t\"left\": {}, \"like\": {}, \"limit\": {}, \"linear\": {}, \"lines\": {}, \"load\": {},\n\t\"localtime\": {}, \"localtimestamp\": {}, \"lock\": {}, \"long\": {}, \"longblob\": {},\n\t\"longtext\": {}, \"loop\": {}, \"low_priority\": {}, \"master_bind\": {},\n\t\"master_ssl_verify_server_cert\": {}, \"match\": {}, \"maxvalue\": {}, \"mediumblob\": {},\n\t\"mediumint\": {}, \"mediumtext\": {}, \"middleint\": {}, \"minute_microsecond\": {},\n\t\"minute_second\": {}, \"mod\": {}, \"modifies\": {}, \"natural\": {}, \"not\": {},\n\t\"no_write_to_binlog\": {}, \"nth_value\": {}, \"ntile\": {}, \"null\": {}, \"numeric\": {},\n\t\"of\": {}, \"on\": {}, \"optimize\": {}, \"optimizer_costs\": {}, \"option\": {},\n\t\"optionally\": {}, \"or\": {}, \"order\": {}, \"out\": {}, \"outer\": {}, \"outfile\": {},\n\t\"over\": {}, \"partition\": {}, \"percent_rank\": {}, \"precision\": {}, \"primary\": {},\n\t\"procedure\": {}, \"purge\": {}, \"range\": {}, \"rank\": {}, \"read\": {}, \"reads\": {},\n\t\"read_write\": {}, \"real\": {}, \"recursive\": {}, \"references\": {}, \"regexp\": {},\n\t\"release\": {}, \"rename\": {}, \"repeat\": {}, \"replace\": {}, \"require\": {},\n\t\"resignal\": {}, \"restrict\": {}, \"return\": {}, \"revoke\": {}, \"right\": {}, \"rlike\": {},\n\t\"row\": {}, \"rows\": {}, \"row_number\": {}, \"schema\": {}, \"schemas\": {},\n\t\"second_microsecond\": {}, \"select\": {}, \"sensitive\": {}, \"separator\": {}, \"set\": {},\n\t\"show\": {}, \"signal\": {}, \"smallint\": {}, \"spatial\": {}, \"specific\": {}, \"sql\": {},\n\t\"sqlexception\": {}, \"sqlstate\": {}, \"sqlwarning\": {}, \"sql_big_result\": {},\n\t\"sql_calc_found_rows\": {}, \"sql_small_result\": {}, \"ssl\": {}, \"starting\": {},\n\t\"stored\": {}, \"straight_join\": {}, \"system\": {}, \"table\": {}, \"terminated\": {},\n\t\"then\": {}, \"tinyblob\": {}, \"tinyint\": {}, \"tinytext\": {}, \"to\": {}, \"trailing\": {},\n\t\"trigger\": {}, \"true\": {}, \"undo\": {}, \"union\": {}, \"unique\": {}, \"unlock\": {},\n\t\"unsigned\": {}, \"update\": {}, \"usage\": {}, \"use\": {}, \"using\": {}, \"utc_date\": {},\n\t\"utc_time\": {}, \"utc_timestamp\": {}, \"values\": {}, \"varbinary\": {}, \"varchar\": {},\n\t\"varcharacter\": {}, \"varying\": {}, \"virtual\": {}, \"when\": {}, \"where\": {},\n\t\"while\": {}, \"window\": {}, \"with\": {}, \"write\": {}, \"xor\": {}, \"year_month\": {},\n\t\"zerofill\": {},\n}\n\n// SQLServer keyword reference:\n// https://learn.microsoft.com/en-us/sql/t-sql/language-elements/reserved-keywords-transact-sql?view=sql-server-ver16\nvar sqlserverKeywords = map[string]struct{}{\n\t\"add\": {}, \"external\": {}, \"procedure\": {}, \"all\": {}, \"fetch\": {}, \"public\": {},\n\t\"alter\": {}, \"file\": {}, \"raiserror\": {}, \"and\": {}, \"fillfactor\": {}, \"read\": {},\n\t\"any\": {}, \"for\": {}, \"readtext\": {}, \"as\": {}, \"foreign\": {}, \"reconfigure\": {},\n\t\"asc\": {}, \"freetext\": {}, \"references\": {}, \"authorization\": {},\n\t\"freetexttable\": {}, \"replication\": {}, \"backup\": {}, \"from\": {}, \"restore\": {},\n\t\"begin\": {}, \"full\": {}, \"restrict\": {}, \"between\": {}, \"function\": {}, \"return\": {},\n\t\"break\": {}, \"goto\": {}, \"revert\": {}, \"browse\": {}, \"grant\": {}, \"revoke\": {},\n\t\"bulk\": {}, \"group\": {}, \"right\": {}, \"by\": {}, \"having\": {}, \"rollback\": {},\n\t\"cascade\": {}, \"holdlock\": {}, \"rowcount\": {}, \"case\": {}, \"identity\": {},\n\t\"rowguidcol\": {}, \"check\": {}, \"identity_insert\": {}, \"rule\": {}, \"checkpoint\": {},\n\t\"identitycol\": {}, \"save\": {}, \"close\": {}, \"if\": {}, \"schema\": {}, \"clustered\": {},\n\t\"in\": {}, \"securityaudit\": {}, \"coalesce\": {}, \"index\": {}, \"select\": {},\n\t\"collate\": {}, \"inner\": {}, \"semantickeyphrasetable\": {}, \"column\": {},\n\t\"insert\": {}, \"semanticsimilaritydetailstable\": {}, \"commit\": {}, \"intersect\": {},\n\t\"semanticsimilaritytable\": {}, \"compute\": {}, \"into\": {}, \"session_user\": {},\n\t\"constraint\": {}, \"is\": {}, \"set\": {}, \"contains\": {}, \"join\": {}, \"setuser\": {},\n\t\"containstable\": {}, \"key\": {}, \"shutdown\": {}, \"continue\": {}, \"kill\": {},\n\t\"some\": {}, \"convert\": {}, \"left\": {}, \"statistics\": {}, \"create\": {}, \"like\": {},\n\t\"system_user\": {}, \"cross\": {}, \"lineno\": {}, \"table\": {}, \"current\": {}, \"load\": {},\n\t\"tablesample\": {}, \"current_date\": {}, \"merge\": {}, \"textsize\": {},\n\t\"current_time\": {}, \"national\": {}, \"then\": {}, \"current_timestamp\": {},\n\t\"nocheck\": {}, \"to\": {}, \"current_user\": {}, \"nonclustered\": {}, \"top\": {},\n\t\"cursor\": {}, \"not\": {}, \"tran\": {}, \"database\": {}, \"null\": {}, \"transaction\": {},\n\t\"dbcc\": {}, \"nullif\": {}, \"trigger\": {}, \"deallocate\": {}, \"of\": {}, \"truncate\": {},\n\t\"declare\": {}, \"off\": {}, \"try_convert\": {}, \"default\": {}, \"offsets\": {},\n\t\"tsequal\": {}, \"delete\": {}, \"on\": {}, \"union\": {}, \"deny\": {}, \"open\": {},\n\t\"unique\": {}, \"desc\": {}, \"opendatasource\": {}, \"unpivot\": {}, \"disk\": {},\n\t\"openquery\": {}, \"update\": {}, \"distinct\": {}, \"openrowset\": {}, \"updatetext\": {},\n\t\"distributed\": {}, \"openxml\": {}, \"use\": {}, \"double\": {}, \"option\": {}, \"user\": {},\n\t\"drop\": {}, \"or\": {}, \"values\": {}, \"dump\": {}, \"order\": {}, \"varying\": {},\n\t\"else\": {}, \"outer\": {}, \"view\": {}, \"end\": {}, \"over\": {}, \"waitfor\": {},\n\t\"errlvl\": {}, \"percent\": {}, \"when\": {}, \"escape\": {}, \"pivot\": {}, \"where\": {},\n\t\"except\": {}, \"plan\": {}, \"while\": {}, \"exec\": {}, \"precision\": {}, \"with\": {},\n\t\"execute\": {}, \"primary\": {}, \"within group\": {}, \"exists\": {}, \"print\": {},\n\t\"writetext\": {}, \"exit\": {}, \"proc\": {},\n}\n"
  },
  {
    "path": "fmt_test.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"flag\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n)\n\nvar (\n\tpostgresDSN  = flag.String(\"postgres\", \"\", \"\")\n\tmysqlDSN     = flag.String(\"mysql\", \"\", \"\")\n\tsqlserverDSN = flag.String(\"sqlserver\", \"\", \"\")\n)\n\nfunc TestWritef(t *testing.T) {\n\ttype TT struct {\n\t\tctx        context.Context\n\t\tdialect    string\n\t\tformat     string\n\t\tvalues     []any\n\t\twantQuery  string\n\t\twantArgs   []any\n\t\twantParams map[string][]int\n\t}\n\n\tassert := func(t *testing.T, tt TT) {\n\t\tif tt.ctx == nil {\n\t\t\ttt.ctx = context.Background()\n\t\t}\n\t\tbuf := new(bytes.Buffer)\n\t\targs := new([]any)\n\t\tparams := make(map[string][]int)\n\t\terr := Writef(tt.ctx, tt.dialect, buf, args, params, tt.format, tt.values)\n\t\tif err != nil {\n\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t}\n\t\tif diff := testutil.Diff(buf.String(), tt.wantQuery); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif len(*args) > 0 || len(tt.wantArgs) > 0 {\n\t\t\tif diff := testutil.Diff(*args, tt.wantArgs); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t}\n\t\tif len(params) > 0 || len(tt.wantParams) > 0 {\n\t\t\tif diff := testutil.Diff(params, tt.wantParams); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t}\n\t}\n\n\tt.Run(\"empty\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"\"\n\t\ttt.values = []any{}\n\t\ttt.wantQuery = \"\"\n\t\ttt.wantArgs = []any{}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"escape curly bracket {{\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"SELECT {} = '{{}'\"\n\t\ttt.values = []any{\"{}\"}\n\t\ttt.wantQuery = `SELECT ? = '{}'`\n\t\ttt.wantArgs = []any{\"{}\"}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"expr\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"(MAX(AVG({one}), AVG({two}), SUM({three})) + {incr}) IN ({slice})\"\n\t\ttt.values = []any{\n\t\t\tsql.Named(\"one\", tmpfield(\"user_id\")),\n\t\t\tsql.Named(\"two\", tmpfield(\"age\")),\n\t\t\tsql.Named(\"three\", tmpfield(\"age\")),\n\t\t\tsql.Named(\"incr\", 1),\n\t\t\tsql.Named(\"slice\", []int{1, 2, 3}),\n\t\t}\n\t\ttt.wantQuery = \"(MAX(AVG(user_id), AVG(age), SUM(age)) + ?) IN (?, ?, ?)\"\n\t\ttt.wantArgs = []any{1, 1, 2, 3}\n\t\ttt.wantParams = map[string][]int{\"incr\": {0}}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"Field slice expansion\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"SELECT {} FROM {}\"\n\t\ttt.values = []any{\n\t\t\t[]Field{\n\t\t\t\ttmpfield(\"111.aaa\"),\n\t\t\t\ttmpfield(\"222.bbb\"),\n\t\t\t\ttmpfield(\"333.ccc\"),\n\t\t\t},\n\t\t\ttmptable(\"public.222\"),\n\t\t}\n\t\ttt.wantQuery = `SELECT \"111\".aaa, \"222\".bbb, \"333\".ccc FROM public.\"222\"`\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"{param}, {param}\" +\n\t\t\t\", {array}, {array}\" +\n\t\t\t\", {bytes}, {bytes}\" +\n\t\t\t\", {bool}, {bool}\" +\n\t\t\t\", {enum}, {enum}\" +\n\t\t\t\", {json}, {json}\" +\n\t\t\t\", {int}, {int}\" +\n\t\t\t\", {int64}, {float64}\" +\n\t\t\t\", {string}, {string}\" +\n\t\t\t\", {time}, {time}\" +\n\t\t\t\", {uuid}, {uuid}\"\n\t\ttt.values = []any{\n\t\t\tParam(\"param\", nil),\n\t\t\tArrayParam(\"array\", []int{1, 2, 3}),\n\t\t\tBytesParam(\"bytes\", []byte{0xFF, 0xFF, 0xFF}),\n\t\t\tBoolParam(\"bool\", true),\n\t\t\tEnumParam(\"enum\", Monday),\n\t\t\tJSONParam(\"json\", map[string]string{\"lorem\": \"ipsum\"}),\n\t\t\tIntParam(\"int\", 5),\n\t\t\tInt64Param(\"int64\", 7),\n\t\t\tFloat64Param(\"float64\", 11.0),\n\t\t\tStringParam(\"string\", \"lorem ipsum\"),\n\t\t\tTimeParam(\"time\", time.Unix(0, 0)),\n\t\t\tUUIDParam(\"uuid\", [16]byte{0xa4, 0xf9, 0x52, 0xf1, 0x4c, 0x45, 0x4e, 0x63, 0xbd, 0x4e, 0x15, 0x9c, 0xa3, 0x3c, 0x8e, 0x20}),\n\t\t}\n\t\ttt.wantQuery = \"?, ?\" +\n\t\t\t\", ?, ?\" +\n\t\t\t\", ?, ?\" +\n\t\t\t\", ?, ?\" +\n\t\t\t\", ?, ?\" +\n\t\t\t\", ?, ?\" +\n\t\t\t\", ?, ?\" +\n\t\t\t\", ?, ?\" +\n\t\t\t\", ?, ?\" +\n\t\t\t\", ?, ?\" +\n\t\t\t\", ?, ?\"\n\t\ttt.wantArgs = []any{\n\t\t\tnil, nil,\n\t\t\t\"[1,2,3]\", \"[1,2,3]\",\n\t\t\t[]byte{0xFF, 0xFF, 0xFF}, []byte{0xFF, 0xFF, 0xFF},\n\t\t\ttrue, true,\n\t\t\t\"Monday\", \"Monday\",\n\t\t\t`{\"lorem\":\"ipsum\"}`, `{\"lorem\":\"ipsum\"}`,\n\t\t\t5, 5,\n\t\t\tint64(7), float64(11.0),\n\t\t\t\"lorem ipsum\", \"lorem ipsum\",\n\t\t\ttime.Unix(0, 0), time.Unix(0, 0),\n\t\t\t[]byte{0xa4, 0xf9, 0x52, 0xf1, 0x4c, 0x45, 0x4e, 0x63, 0xbd, 0x4e, 0x15, 0x9c, 0xa3, 0x3c, 0x8e, 0x20},\n\t\t\t[]byte{0xa4, 0xf9, 0x52, 0xf1, 0x4c, 0x45, 0x4e, 0x63, 0xbd, 0x4e, 0x15, 0x9c, 0xa3, 0x3c, 0x8e, 0x20},\n\t\t}\n\t\ttt.wantParams = map[string][]int{\n\t\t\t\"param\":   {0, 1},\n\t\t\t\"array\":   {2, 3},\n\t\t\t\"bytes\":   {4, 5},\n\t\t\t\"bool\":    {6, 7},\n\t\t\t\"enum\":    {8, 9},\n\t\t\t\"json\":    {10, 11},\n\t\t\t\"int\":     {12, 13},\n\t\t\t\"int64\":   {14},\n\t\t\t\"float64\": {15},\n\t\t\t\"string\":  {16, 17},\n\t\t\t\"time\":    {18, 19},\n\t\t\t\"uuid\":    {20, 21},\n\t\t}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"duplicate params should error\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"{param}, {param}\"\n\t\ttt.values = []any{\n\t\t\tParam(\"param\", 1),\n\t\t\tParam(\"param\", 1),\n\t\t}\n\t\tvar buf bytes.Buffer\n\t\tvar args []any\n\t\tparams := make(map[string][]int)\n\t\tformat := \"{param}, {param}\"\n\t\tvalues := []any{\n\t\t\tParam(\"param\", 1),\n\t\t\tParam(\"param\", 1),\n\t\t}\n\t\terr := Writef(context.Background(), \"\", &buf, &args, params, format, values)\n\t\tif err == nil {\n\t\t\tt.Errorf(testutil.Callers() + \" expected error but got nil\")\n\t\t}\n\t})\n\n\tt.Run(\"sqlite,postgres QuoteIdentifier\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.format = \"SELECT {}\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(`\"; \"\"; DROP TABLE users --`),\n\t\t}\n\t\ttt.wantQuery = `SELECT \"\"\"; \"\"; DROP TABLE users --\"`\n\t\tassert(t, tt)\n\n\t\ttt.dialect = DialectPostgres\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlite,postgres anonymous params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.format = \"SELECT {}\" +\n\t\t\t\" FROM {}\" +\n\t\t\t\" WHERE {} = {}\" +\n\t\t\t\" AND {} <> {}\" +\n\t\t\t\" AND {} IN ({})\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"name\"),\n\t\t\ttmptable(\"users\"),\n\t\t\ttmpfield(\"age\"), 5,\n\t\t\ttmpfield(\"email\"), \"bob@email.com\",\n\t\t\ttmpfield(\"name\"), []string{\"tom\", \"dick\", \"harry\"},\n\t\t}\n\t\ttt.wantQuery = \"SELECT name\" +\n\t\t\t\" FROM users\" +\n\t\t\t\" WHERE age = $1\" +\n\t\t\t\" AND email <> $2\" +\n\t\t\t\" AND name IN ($3, $4, $5)\"\n\t\ttt.wantArgs = []any{5, \"bob@email.com\", \"tom\", \"dick\", \"harry\"}\n\t\tassert(t, tt)\n\n\t\ttt.dialect = DialectPostgres\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlite,postgres ordinal params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.format = \"SELECT {}\" +\n\t\t\t\" FROM {}\" +\n\t\t\t\" WHERE {} = {5}\" +\n\t\t\t\" AND {} <> {5}\" +\n\t\t\t\" AND {1} IN ({6})\" +\n\t\t\t\" AND {4} IN ({6})\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"name\"),\n\t\t\ttmptable(\"users\"),\n\t\t\ttmpfield(\"age\"),\n\t\t\ttmpfield(\"email\"),\n\t\t\t\"bob@email.com\",\n\t\t\t[]string{\"tom\", \"dick\", \"harry\"},\n\t\t}\n\t\ttt.wantQuery = \"SELECT name\" +\n\t\t\t\" FROM users\" +\n\t\t\t\" WHERE age = $1\" +\n\t\t\t\" AND email <> $1\" +\n\t\t\t\" AND name IN ($2, $3, $4)\" +\n\t\t\t\" AND email IN ($5, $6, $7)\"\n\t\ttt.wantArgs = []any{\n\t\t\t\"bob@email.com\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t}\n\t\tassert(t, tt)\n\n\t\ttt.dialect = DialectPostgres\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlite named params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.format = \"SELECT {}\" +\n\t\t\t\" FROM {}\" +\n\t\t\t\" WHERE {3} = {age}\" +\n\t\t\t\" AND {3} > {6}\" +\n\t\t\t\" AND {4} <> {email}\" +\n\t\t\t\" AND {1} IN ({names})\" +\n\t\t\t\" AND {4} IN ({names})\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"name\"),\n\t\t\ttmptable(\"users\"),\n\t\t\ttmpfield(\"age\"),\n\t\t\ttmpfield(\"email\"),\n\t\t\tsql.Named(\"email\", \"bob@email.com\"),\n\t\t\tsql.Named(\"age\", 5),\n\t\t\tsql.Named(\"names\", []string{\"tom\", \"dick\", \"harry\"}),\n\t\t}\n\t\ttt.wantQuery = \"SELECT name\" +\n\t\t\t\" FROM users\" +\n\t\t\t\" WHERE age = $age\" +\n\t\t\t\" AND age > $age\" +\n\t\t\t\" AND email <> $email\" +\n\t\t\t\" AND name IN ($3, $4, $5)\" +\n\t\t\t\" AND email IN ($6, $7, $8)\"\n\t\ttt.wantArgs = []any{\n\t\t\tsql.Named(\"age\", 5),\n\t\t\tsql.Named(\"email\", \"bob@email.com\"),\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t}\n\t\ttt.wantParams = map[string][]int{\"age\": {0}, \"email\": {1}}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"postgres named params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectPostgres\n\t\ttt.format = \"SELECT {}\" +\n\t\t\t\" FROM {}\" +\n\t\t\t\" WHERE {3} = {age}\" +\n\t\t\t\" AND {3} > {6}\" +\n\t\t\t\" AND {4} <> {email}\" +\n\t\t\t\" AND {1} IN ({names})\" +\n\t\t\t\" AND {4} IN ({names})\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"name\"),\n\t\t\ttmptable(\"users\"),\n\t\t\ttmpfield(\"age\"),\n\t\t\ttmpfield(\"email\"),\n\t\t\tsql.Named(\"email\", \"bob@email.com\"),\n\t\t\tsql.Named(\"age\", 5),\n\t\t\tsql.Named(\"names\", []string{\"tom\", \"dick\", \"harry\"}),\n\t\t}\n\t\ttt.wantQuery = \"SELECT name\" +\n\t\t\t\" FROM users\" +\n\t\t\t\" WHERE age = $1\" +\n\t\t\t\" AND age > $1\" +\n\t\t\t\" AND email <> $2\" +\n\t\t\t\" AND name IN ($3, $4, $5)\" +\n\t\t\t\" AND email IN ($6, $7, $8)\"\n\t\ttt.wantArgs = []any{\n\t\t\t5,\n\t\t\t\"bob@email.com\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t}\n\t\ttt.wantParams = map[string][]int{\"age\": {0}, \"email\": {1}}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlite,postgres SQLWriter in named param\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.format = \"SELECT {field} FROM {tbl} WHERE {field} IN ({nums})\"\n\t\ttt.values = []any{\n\t\t\tsql.Named(\"nums\", []int{1, 2, 3}),\n\t\t\tsql.Named(\"tbl\", tmptable(\"public.tbl\")),\n\t\t\tsql.Named(\"field\", tmpfield(\"tbl.field\")),\n\t\t}\n\t\ttt.wantQuery = `SELECT tbl.field FROM public.tbl WHERE tbl.field IN ($1, $2, $3)`\n\t\ttt.wantArgs = []any{1, 2, 3}\n\t\tassert(t, tt)\n\n\t\ttt.dialect = DialectPostgres\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"mysql QuoteIdentifier\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectMySQL\n\t\ttt.format = \"SELECT {}\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"`; ``; DROP TABLE users --\"),\n\t\t}\n\t\ttt.wantQuery = \"SELECT ```; ``; DROP TABLE users --`\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"mysql anonymous params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectMySQL\n\t\ttt.format = \"SELECT {}\" +\n\t\t\t\" FROM {}\" +\n\t\t\t\" WHERE {} = {}\" +\n\t\t\t\" AND {} <> {}\" +\n\t\t\t\" AND {} IN ({})\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"name\"),\n\t\t\ttmptable(\"users\"),\n\t\t\ttmpfield(\"age\"), 5,\n\t\t\ttmpfield(\"email\"), \"bob@email.com\",\n\t\t\ttmpfield(\"name\"), []string{\"tom\", \"dick\", \"harry\"},\n\t\t}\n\t\ttt.wantQuery = \"SELECT name\" +\n\t\t\t\" FROM users\" +\n\t\t\t\" WHERE age = ?\" +\n\t\t\t\" AND email <> ?\" +\n\t\t\t\" AND name IN (?, ?, ?)\"\n\t\ttt.wantArgs = []any{5, \"bob@email.com\", \"tom\", \"dick\", \"harry\"}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"mysql ordinal params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectMySQL\n\t\ttt.format = \"SELECT {}\" +\n\t\t\t\" FROM {}\" +\n\t\t\t\" WHERE {} = {5}\" +\n\t\t\t\" AND {} <> {5}\" +\n\t\t\t\" AND {1} IN ({6})\" +\n\t\t\t\" AND {4} IN ({6})\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"name\"),\n\t\t\ttmptable(\"users\"),\n\t\t\ttmpfield(\"age\"),\n\t\t\ttmpfield(\"email\"),\n\t\t\t\"bob@email.com\",\n\t\t\t[]string{\"tom\", \"dick\", \"harry\"},\n\t\t}\n\t\ttt.wantQuery = \"SELECT name\" +\n\t\t\t\" FROM users\" +\n\t\t\t\" WHERE age = ?\" +\n\t\t\t\" AND email <> ?\" +\n\t\t\t\" AND name IN (?, ?, ?)\" +\n\t\t\t\" AND email IN (?, ?, ?)\"\n\t\ttt.wantArgs = []any{\n\t\t\t\"bob@email.com\", \"bob@email.com\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"mysql named params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectMySQL\n\t\ttt.format = \"SELECT {}\" +\n\t\t\t\" FROM {}\" +\n\t\t\t\" WHERE {3} = {age}\" +\n\t\t\t\" AND {3} > {6}\" +\n\t\t\t\" AND {4} <> {email}\" +\n\t\t\t\" AND {1} IN ({names})\" +\n\t\t\t\" AND {4} IN ({names})\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"name\"),\n\t\t\ttmptable(\"users\"),\n\t\t\ttmpfield(\"age\"),\n\t\t\ttmpfield(\"email\"),\n\t\t\tsql.Named(\"email\", \"bob@email.com\"),\n\t\t\tsql.Named(\"age\", 5),\n\t\t\tsql.Named(\"names\", []string{\"tom\", \"dick\", \"harry\"}),\n\t\t}\n\t\ttt.wantQuery = \"SELECT name\" +\n\t\t\t\" FROM users\" +\n\t\t\t\" WHERE age = ?\" +\n\t\t\t\" AND age > ?\" +\n\t\t\t\" AND email <> ?\" +\n\t\t\t\" AND name IN (?, ?, ?)\" +\n\t\t\t\" AND email IN (?, ?, ?)\"\n\t\ttt.wantArgs = []any{\n\t\t\t5, 5,\n\t\t\t\"bob@email.com\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t}\n\t\ttt.wantParams = map[string][]int{\"age\": {0, 1}, \"email\": {2}}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"mysql SQLWriter in named param\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectMySQL\n\t\ttt.format = \"SELECT {field} FROM {tbl} WHERE {field} IN ({nums})\"\n\t\ttt.values = []any{\n\t\t\tsql.Named(\"nums\", []int{1, 2, 3}),\n\t\t\tsql.Named(\"tbl\", tmptable(\"public.tbl\")),\n\t\t\tsql.Named(\"field\", tmpfield(\"tbl.field\")),\n\t\t}\n\t\ttt.wantQuery = `SELECT tbl.field FROM public.tbl WHERE tbl.field IN (?, ?, ?)`\n\t\ttt.wantArgs = []any{1, 2, 3}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlserver QuoteIdentifier\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLServer\n\t\ttt.format = \"SELECT {}\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"]; ]]; DROP TABLE users --\"),\n\t\t}\n\t\ttt.wantQuery = \"SELECT []]; ]]; DROP TABLE users --]\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlserver anonymous params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLServer\n\t\ttt.format = \"SELECT {}\" +\n\t\t\t\" FROM {}\" +\n\t\t\t\" WHERE {} = {}\" +\n\t\t\t\" AND {} <> {}\" +\n\t\t\t\" AND {} IN ({})\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"name\"),\n\t\t\ttmptable(\"users\"),\n\t\t\ttmpfield(\"age\"), 5,\n\t\t\ttmpfield(\"email\"), \"bob@email.com\",\n\t\t\ttmpfield(\"name\"), []string{\"tom\", \"dick\", \"harry\"},\n\t\t}\n\t\ttt.wantQuery = \"SELECT name\" +\n\t\t\t\" FROM users\" +\n\t\t\t\" WHERE age = @p1\" +\n\t\t\t\" AND email <> @p2\" +\n\t\t\t\" AND name IN (@p3, @p4, @p5)\"\n\t\ttt.wantArgs = []any{5, \"bob@email.com\", \"tom\", \"dick\", \"harry\"}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlserver ordinal params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLServer\n\t\ttt.format = \"SELECT {}\" +\n\t\t\t\" FROM {}\" +\n\t\t\t\" WHERE {} = {5}\" +\n\t\t\t\" AND {} <> {5}\" +\n\t\t\t\" AND {1} IN ({6})\" +\n\t\t\t\" AND {4} IN ({6})\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"name\"),\n\t\t\ttmptable(\"users\"),\n\t\t\ttmpfield(\"age\"),\n\t\t\ttmpfield(\"email\"),\n\t\t\t\"bob@email.com\",\n\t\t\t[]string{\"tom\", \"dick\", \"harry\"},\n\t\t}\n\t\ttt.wantQuery = \"SELECT name\" +\n\t\t\t\" FROM users\" +\n\t\t\t\" WHERE age = @p1\" +\n\t\t\t\" AND email <> @p1\" +\n\t\t\t\" AND name IN (@p2, @p3, @p4)\" +\n\t\t\t\" AND email IN (@p5, @p6, @p7)\"\n\t\ttt.wantArgs = []any{\n\t\t\t\"bob@email.com\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlserver named params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLServer\n\t\ttt.format = \"SELECT {}\" +\n\t\t\t\" FROM {}\" +\n\t\t\t\" WHERE {3} = {age}\" +\n\t\t\t\" AND {3} > {6}\" +\n\t\t\t\" AND {4} <> {email}\" +\n\t\t\t\" AND {1} IN ({names})\" +\n\t\t\t\" AND {4} IN ({names})\"\n\t\ttt.values = []any{\n\t\t\ttmpfield(\"name\"),\n\t\t\ttmptable(\"users\"),\n\t\t\ttmpfield(\"age\"),\n\t\t\ttmpfield(\"email\"),\n\t\t\tsql.Named(\"email\", \"bob@email.com\"),\n\t\t\tsql.Named(\"age\", 5),\n\t\t\tsql.Named(\"names\", []string{\"tom\", \"dick\", \"harry\"}),\n\t\t}\n\t\ttt.wantQuery = \"SELECT name\" +\n\t\t\t\" FROM users\" +\n\t\t\t\" WHERE age = @age\" +\n\t\t\t\" AND age > @age\" +\n\t\t\t\" AND email <> @email\" +\n\t\t\t\" AND name IN (@p3, @p4, @p5)\" +\n\t\t\t\" AND email IN (@p6, @p7, @p8)\"\n\t\ttt.wantArgs = []any{\n\t\t\tsql.Named(\"age\", 5),\n\t\t\tsql.Named(\"email\", \"bob@email.com\"),\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t\t\"tom\", \"dick\", \"harry\",\n\t\t}\n\t\ttt.wantParams = map[string][]int{\"age\": {0}, \"email\": {1}}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlserver SQLWriter in named param\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLServer\n\t\ttt.format = \"SELECT {field} FROM {tbl} WHERE {field} IN ({nums})\"\n\t\ttt.values = []any{\n\t\t\tsql.Named(\"nums\", []int{1, 2, 3}),\n\t\t\tsql.Named(\"tbl\", tmptable(\"dbo.tbl\")),\n\t\t\tsql.Named(\"field\", tmpfield(\"tbl.field\")),\n\t\t}\n\t\ttt.wantQuery = `SELECT tbl.field FROM dbo.tbl WHERE tbl.field IN (@p1, @p2, @p3)`\n\t\ttt.wantArgs = []any{1, 2, 3}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"preprocessValue kicks in for anonymous, ordinal params, named params and slices\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.format = \"SELECT {}, {2}, {foo}, {3}, {bar}\"\n\t\ttt.values = []any{\n\t\t\tMonday,\n\t\t\tsql.Named(\"foo\", Tuesday),\n\t\t\tWednesday,\n\t\t\tsql.Named(\"bar\", []Weekday{Thursday, Friday, Saturday}),\n\t\t}\n\t\ttt.wantQuery = \"SELECT $1, $foo, $foo, $3, $4, $5, $6\"\n\t\ttt.wantArgs = []any{\n\t\t\t\"Monday\",\n\t\t\tsql.NamedArg{Name: \"foo\", Value: \"Tuesday\"},\n\t\t\t\"Wednesday\",\n\t\t\t\"Thursday\",\n\t\t\t\"Friday\",\n\t\t\t\"Saturday\",\n\t\t}\n\t\ttt.wantParams = map[string][]int{\"foo\": {1}}\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"no closing curly brace }\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"SELECT {field\"\n\t\tbuf := new(bytes.Buffer)\n\t\targs := new([]any)\n\t\tparams := make(map[string][]int)\n\t\terr := Writef(tt.ctx, tt.dialect, buf, args, params, tt.format, tt.values)\n\t\tif err == nil {\n\t\t\tt.Error(testutil.Callers(), \"expected error but got nil\")\n\t\t}\n\t})\n\n\tt.Run(\"too few values passed in\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"SELECT {}, {}, {}, {}\"\n\t\ttt.values = []any{1, 2}\n\t\tbuf := new(bytes.Buffer)\n\t\targs := new([]any)\n\t\tparams := make(map[string][]int)\n\t\terr := Writef(tt.ctx, tt.dialect, buf, args, params, tt.format, tt.values)\n\t\tif err == nil {\n\t\t\tt.Error(testutil.Callers(), \"expected error but got nil\")\n\t\t}\n\t})\n\n\tt.Run(\"anonymous param faulty SQL\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"SELECT {}\"\n\t\ttt.values = []any{FaultySQL{}}\n\t\tbuf := new(bytes.Buffer)\n\t\targs := new([]any)\n\t\tparams := make(map[string][]int)\n\t\terr := Writef(tt.ctx, tt.dialect, buf, args, params, tt.format, tt.values)\n\t\tif !errors.Is(err, ErrFaultySQL) {\n\t\t\tt.Error(testutil.Callers(), \"expected ErrFaultySQL but got %v\", err)\n\t\t}\n\t})\n\n\tt.Run(\"ordinal param faulty SQL\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"SELECT {1}\"\n\t\ttt.values = []any{FaultySQL{}}\n\t\tbuf := new(bytes.Buffer)\n\t\targs := new([]any)\n\t\tparams := make(map[string][]int)\n\t\terr := Writef(tt.ctx, tt.dialect, buf, args, params, tt.format, tt.values)\n\t\tif !errors.Is(err, ErrFaultySQL) {\n\t\t\tt.Error(testutil.Callers(), \"expected ErrFaultySQL but got %v\", err)\n\t\t}\n\t})\n\n\tt.Run(\"named param faulty SQL\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"SELECT {field}\"\n\t\ttt.values = []any{sql.Named(\"field\", FaultySQL{})}\n\t\tbuf := new(bytes.Buffer)\n\t\targs := new([]any)\n\t\tparams := make(map[string][]int)\n\t\terr := Writef(tt.ctx, tt.dialect, buf, args, params, tt.format, tt.values)\n\t\tif !errors.Is(err, ErrFaultySQL) {\n\t\t\tt.Error(testutil.Callers(), \"expected ErrFaultySQL but got %v\", err)\n\t\t}\n\t})\n\n\tt.Run(\"ordinal param out of bounds\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"SELECT {1}, {2}, {99}\"\n\t\ttt.values = []any{1, 2, 3}\n\t\tbuf := new(bytes.Buffer)\n\t\targs := new([]any)\n\t\tparams := make(map[string][]int)\n\t\terr := Writef(tt.ctx, tt.dialect, buf, args, params, tt.format, tt.values)\n\t\tif err == nil {\n\t\t\tt.Error(testutil.Callers(), \"expected error but got nil\")\n\t\t}\n\t})\n\n\tt.Run(\"nonexistent named param\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"SELECT {A}, {B}, {C}\"\n\t\ttt.values = []any{\n\t\t\tsql.Named(\"A\", 1),\n\t\t\tsql.Named(\"B\", 2),\n\t\t\tsql.Named(\"E\", 5),\n\t\t}\n\t\tbuf := new(bytes.Buffer)\n\t\targs := new([]any)\n\t\tparams := make(map[string][]int)\n\t\terr := Writef(tt.ctx, tt.dialect, buf, args, params, tt.format, tt.values)\n\t\tif err == nil {\n\t\t\tt.Error(testutil.Callers(), \"expected error but got nil\")\n\t\t}\n\t})\n\n\tt.Run(\"expandSlice faulty SQL\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.format = \"SELECT {}\"\n\t\ttt.values = []any{\n\t\t\t[]Field{tmpfield(\"name\"), tmpfield(\"age\"), FaultySQL{}},\n\t\t}\n\t\tbuf := new(bytes.Buffer)\n\t\targs := new([]any)\n\t\tparams := make(map[string][]int)\n\t\terr := Writef(tt.ctx, tt.dialect, buf, args, params, tt.format, tt.values)\n\t\tif !errors.Is(err, ErrFaultySQL) {\n\t\t\tt.Error(testutil.Callers(), \"expected ErrFaultySQL but got %v\", err)\n\t\t}\n\t})\n}\n\nfunc TestSprintf(t *testing.T) {\n\ttype TT struct {\n\t\tdialect    string\n\t\tquery      string\n\t\targs       []any\n\t\twantString string\n\t}\n\n\tassert := func(t *testing.T, tt TT) {\n\t\tgotString, err := Sprintf(tt.dialect, tt.query, tt.args)\n\t\tif err != nil {\n\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t}\n\t\tif diff := testutil.Diff(gotString, tt.wantString); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t}\n\n\tassertNotOK := func(t *testing.T, tt TT) {\n\t\t_, err := Sprintf(tt.dialect, tt.query, tt.args)\n\t\tif err == nil {\n\t\t\tt.Fatal(testutil.Callers(), \"expected error but got nil\")\n\t\t}\n\t}\n\n\tt.Run(\"empty\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = \"\"\n\t\ttt.query = \"\"\n\t\ttt.args = []any{}\n\t\ttt.wantString = \"\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"insideString, insideIdentifier and escaping single quotes\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = \"\"\n\t\ttt.query = `SELECT ?` +\n\t\t\t`, 'do not \"rebind\" ? ? ?'` + // string\n\t\t\t`, \"do not 'rebind' ? ? ?\"` + // identifier\n\t\t\t`, ?` +\n\t\t\t`, ?`\n\t\ttt.args = []any{\n\t\t\t\"normal string\",\n\t\t\t\"string with 'quotes' must be escaped\",\n\t\t\t\"string with already escaped ''quotes'' except for 'this'\",\n\t\t}\n\t\ttt.wantString = `SELECT 'normal string'` +\n\t\t\t`, 'do not \"rebind\" ? ? ?'` +\n\t\t\t`, \"do not 'rebind' ? ? ?\"` +\n\t\t\t`, 'string with ''quotes'' must be escaped'` +\n\t\t\t`, 'string with already escaped ''''quotes'''' except for ''this'''`\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"insideString, insideIdentifier and escaping single quotes (dialect == mysql)\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectMySQL\n\t\ttt.query = `SELECT ?` +\n\t\t\t`, 'do not \"rebind\" ? ? ?'` + // string\n\t\t\t\", `do not \\\" 'rebind' ? ? ?`\" + // identifier\n\t\t\t\", \\\"do not ``` 'rebind' ? ? ?\\\"\" + // identifier\n\t\t\t`, ?` +\n\t\t\t`, ?`\n\t\ttt.args = []any{\n\t\t\t\"normal string\",\n\t\t\t\"string with 'quotes' must be escaped\",\n\t\t\t\"string with already escaped ''quotes'' except for 'this'\",\n\t\t}\n\t\ttt.wantString = `SELECT 'normal string'` +\n\t\t\t`, 'do not \"rebind\" ? ? ?'` +\n\t\t\t\", `do not \\\" 'rebind' ? ? ?`\" +\n\t\t\t\", \\\"do not ``` 'rebind' ? ? ?\\\"\" +\n\t\t\t`, 'string with ''quotes'' must be escaped'` +\n\t\t\t`, 'string with already escaped ''''quotes'''' except for ''this'''`\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"insideString, insideIdentifier and escaping single quotes (dialect == sqlserver)\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLServer\n\t\ttt.query = `SELECT ?` +\n\t\t\t`, 'do not [[rebind] @p1 @p2 @name'` + // string\n\t\t\t\", [do not \\\" 'rebind' [[[[[@pp]] @p3 @p1]\" + // identifier\n\t\t\t\", \\\"do not [[[ 'rebind' [[[[[@pp]] @p3 @p1\\\"\" + // identifier\n\t\t\t`, ?` +\n\t\t\t`, @p3`\n\t\ttt.args = []any{\n\t\t\t\"normal string\",\n\t\t\t\"string with 'quotes' must be escaped\",\n\t\t\t\"string with already escaped ''quotes'' except for 'this'\",\n\t\t}\n\t\ttt.wantString = `SELECT 'normal string'` +\n\t\t\t`, 'do not [[rebind] @p1 @p2 @name'` +\n\t\t\t\", [do not \\\" 'rebind' [[[[[@pp]] @p3 @p1]\" +\n\t\t\t\", \\\"do not [[[ 'rebind' [[[[[@pp]] @p3 @p1\\\"\" +\n\t\t\t`, 'string with ''quotes'' must be escaped'` +\n\t\t\t`, 'string with already escaped ''''quotes'''' except for ''this'''`\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"mysql\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectMySQL\n\t\ttt.query = \"SELECT name FROM users WHERE age = ? AND email <> ? AND name IN (?, ?, ?)\"\n\t\ttt.args = []any{5, \"bob@email.com\", \"tom\", \"dick\", \"harry\"}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND email <> 'bob@email.com' AND name IN ('tom', 'dick', 'harry')\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"mysql insideString\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectMySQL\n\t\ttt.query = \"SELECT name FROM users WHERE age = ? AND email <> '? ? ? ? ''bruh ?' AND name IN (?, ?) ?\"\n\t\ttt.args = []any{5, \"tom\", \"dick\", \"harry\"}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND email <> '? ? ? ? ''bruh ?' AND name IN ('tom', 'dick') 'harry'\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"omitted dialect insideString\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = \"\"\n\t\ttt.query = \"SELECT name FROM users WHERE age = ? AND email <> '? ? ? ? ''bruh ?' AND name IN (?, ?) ?\"\n\t\ttt.args = []any{5, \"tom\", \"dick\", \"harry\"}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND email <> '? ? ? ? ''bruh ?' AND name IN ('tom', 'dick') 'harry'\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"postgres\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectPostgres\n\t\ttt.query = \"SELECT name FROM users WHERE age = $1 AND email <> $2 AND name IN ($2, $3, $4, $1)\"\n\t\ttt.args = []any{5, \"tom\", \"dick\", \"harry\"}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND email <> 'tom' AND name IN ('tom', 'dick', 'harry', 5)\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"postgres insideString\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectPostgres\n\t\ttt.query = \"SELECT name FROM users WHERE age = $1 AND email <> '$2 $2 $3 $4 ''bruh $1' AND name IN ($2, $3) $4\"\n\t\ttt.args = []any{5, \"tom\", \"dick\", \"harry\"}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND email <> '$2 $2 $3 $4 ''bruh $1' AND name IN ('tom', 'dick') 'harry'\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlite\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.query = \"SELECT name FROM users WHERE age = $1 AND email <> $2 AND name IN ($2, $3, $4, $1)\"\n\t\ttt.args = []any{5, \"tom\", \"dick\", \"harry\"}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND email <> 'tom' AND name IN ('tom', 'dick', 'harry', 5)\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlite insideString\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.query = \"SELECT name FROM users WHERE age = $1 AND email <> '$2 $2 $3 $4 ''bruh $1' AND name IN ($2, $3) $4\"\n\t\ttt.args = []any{5, \"tom\", \"dick\", \"harry\"}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND email <> '$2 $2 $3 $4 ''bruh $1' AND name IN ('tom', 'dick') 'harry'\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlite mixing ordinal param and named param\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.query = \"SELECT name FROM users WHERE age = $age AND age > $1 AND email <> $email\"\n\t\ttt.args = []any{sql.Named(\"age\", 5), sql.Named(\"email\", \"bob@email.com\")}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND age > 5 AND email <> 'bob@email.com'\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlite supports everything\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.query = \"SELECT name FROM users WHERE age = ?age AND email <> :email AND name IN (@3, ?4, $5, :5) ? ?\"\n\t\ttt.args = []any{sql.Named(\"age\", 5), sql.Named(\"email\", \"bob@email.com\"), \"tom\", \"dick\", \"harry\"}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND email <> 'bob@email.com' AND name IN ('tom', 'dick', 'harry', 'harry') 5 'bob@email.com'\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlserver\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLServer\n\t\ttt.query = \"SELECT name FROM users WHERE age = @p1 AND email <> @P2 AND name IN (@p2, @p3, @p4, @P1)\"\n\t\ttt.args = []any{5, \"tom\", \"dick\", \"harry\"}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND email <> 'tom' AND name IN ('tom', 'dick', 'harry', 5)\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlserver insideString\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLServer\n\t\ttt.query = \"SELECT name FROM users WHERE age = @p1 AND email <> '@p2 @p2 @p3 @p4 ''bruh @p1' AND name IN (@p2, @p3) @p4\"\n\t\ttt.args = []any{5, \"tom\", \"dick\", \"harry\"}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND email <> '@p2 @p2 @p3 @p4 ''bruh @p1' AND name IN ('tom', 'dick') 'harry'\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"sqlserver mixing ordinal param and named param\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLServer\n\t\ttt.query = \"SELECT name FROM users WHERE age = @age AND age > @p1 AND email <> @email\"\n\t\ttt.args = []any{sql.Named(\"age\", 5), sql.Named(\"email\", \"bob@email.com\")}\n\t\ttt.wantString = \"SELECT name FROM users WHERE age = 5 AND age > 5 AND email <> 'bob@email.com'\"\n\t\tassert(t, tt)\n\t})\n\n\tt.Run(\"unclosed string and identifier\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\t// unclosed string\n\t\ttt.query = `SELECT ?, 'mary had a little', 'lamb`\n\t\ttt.args = []any{1}\n\t\tassertNotOK(t, tt)\n\n\t\t// unclosed identifier\n\t\ttt.query = `SELECT ?, \"one\", \"two\", \"three`\n\t\ttt.args = []any{2}\n\t\tassertNotOK(t, tt)\n\t})\n\n\tt.Run(\"sqlite invalid anonymous param\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.args = []any{23}\n\t\ttt.wantString = \"SELECT 23\"\n\n\t\t// ?1 is valid\n\t\ttt.query = \"SELECT ?1\"\n\t\tassert(t, tt)\n\n\t\t// ? is valid\n\t\ttt.query = \"SELECT ?\"\n\t\tassert(t, tt)\n\n\t\t// $1 is valid\n\t\ttt.query = \"SELECT $1\"\n\t\tassert(t, tt)\n\n\t\t// $ is invalid\n\t\ttt.query = \"SELECT $\"\n\t\tassertNotOK(t, tt)\n\t})\n\n\tt.Run(\"not enough params\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.query = \"SELECT ?, ?, ?\"\n\t\ttt.args = []any{1, 2}\n\t\tassertNotOK(t, tt)\n\t})\n\n\tt.Run(\"functions cannot be printed\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.query = \"SELECT ?, ?\"\n\t\ttt.args = []any{1, func() {}}\n\t\tassertNotOK(t, tt)\n\t})\n\n\tt.Run(\"channels cannot be printed\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.query = \"SELECT ?, ?\"\n\t\ttt.args = []any{make(chan int), 2}\n\t\tassertNotOK(t, tt)\n\t})\n\n\tt.Run(\"non driver.Valuer types cannot be printed\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.query = \"SELECT ?, ?\"\n\t\ttt.args = []any{struct{}{}, any(nil)}\n\t\tassertNotOK(t, tt)\n\t})\n\n\tt.Run(\"ordinal param out of bounds\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.query = \"SELECT @1, @2, @3\"\n\t\ttt.args = []any{1, 2}\n\t\tassertNotOK(t, tt)\n\t})\n\n\tt.Run(\"dialect that does not support sql.NamedArg\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectPostgres\n\t\ttt.query = \"SELECT $test\"\n\t\ttt.args = []any{sql.Named(\"test\", 123)}\n\t\tassertNotOK(t, tt)\n\t})\n\n\tt.Run(\"sql.NamedArg not provided\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TT\n\t\ttt.dialect = DialectSQLite\n\t\ttt.query = \"SELECT :one, :two, :three\"\n\t\ttt.args = []any{\n\t\t\tsql.Named(\"one\", 1),\n\t\t\tsql.Named(\"two\", 2),\n\t\t\tsql.Named(\"four\", 4),\n\t\t}\n\t\tassertNotOK(t, tt)\n\t})\n}\n\nfunc TestSprint(t *testing.T) {\n\ttype TT struct {\n\t\tdescription string\n\t\tdialect     string\n\t\tvalue       any\n\t\twantString  string\n\t}\n\tsingaporeLocation, _ := time.LoadLocation(\"Asia/Singapore\")\n\n\ttests := []TT{{\n\t\tdescription: \"nil\",\n\t\tvalue:       nil,\n\t\twantString:  \"NULL\",\n\t}, {\n\t\tdescription: \"true\",\n\t\tvalue:       true,\n\t\twantString:  \"TRUE\",\n\t}, {\n\t\tdescription: \"false\",\n\t\tvalue:       false,\n\t\twantString:  \"FALSE\",\n\t}, {\n\t\tdescription: \"sqlserver true\",\n\t\tdialect:     DialectSQLServer,\n\t\tvalue:       true,\n\t\twantString:  \"1\",\n\t}, {\n\t\tdescription: \"sqlserver false\",\n\t\tdialect:     DialectSQLServer,\n\t\tvalue:       false,\n\t\twantString:  \"0\",\n\t}, {\n\t\tdescription: \"postgres []byte\",\n\t\tdialect:     DialectPostgres,\n\t\tvalue:       []byte{0xff, 0xff},\n\t\twantString:  `'\\xffff'`,\n\t}, {\n\t\tdescription: \"[]byte\",\n\t\tvalue:       []byte{0xff, 0xff},\n\t\twantString:  `x'ffff'`,\n\t}, {\n\t\tdescription: \"string\",\n\t\tvalue:       \"' OR ''test' = '; DROP TABLE users; -- \",\n\t\twantString:  `''' OR ''''test'' = ''; DROP TABLE users; -- '`,\n\t}, {\n\t\tdescription: \"time.Time\",\n\t\tvalue:       time.Unix(0, 0).UTC(),\n\t\twantString:  `'1970-01-01 00:00:00'`,\n\t}, {\n\t\tdescription: \"time.Time (SQLServer)\",\n\t\tdialect:     DialectSQLServer,\n\t\tvalue:       time.Unix(0, 0).UTC(),\n\t\twantString:  `'1970-01-01 00:00:00+00:00'`,\n\t}, {\n\t\tdescription: \"int\",\n\t\tvalue:       int(0),\n\t\twantString:  `0`,\n\t}, {\n\t\tdescription: \"int8\",\n\t\tvalue:       int8(8),\n\t\twantString:  `8`,\n\t}, {\n\t\tdescription: \"int16\",\n\t\tvalue:       int16(16),\n\t\twantString:  `16`,\n\t}, {\n\t\tdescription: \"int32\",\n\t\tvalue:       int32(32),\n\t\twantString:  `32`,\n\t}, {\n\t\tdescription: \"int64\",\n\t\tvalue:       int64(64),\n\t\twantString:  `64`,\n\t}, {\n\t\tdescription: \"uint\",\n\t\tvalue:       uint(0),\n\t\twantString:  `0`,\n\t}, {\n\t\tdescription: \"uint8\",\n\t\tvalue:       uint8(8),\n\t\twantString:  `8`,\n\t}, {\n\t\tdescription: \"uint16\",\n\t\tvalue:       uint16(16),\n\t\twantString:  `16`,\n\t}, {\n\t\tdescription: \"uint32\",\n\t\tvalue:       uint32(32),\n\t\twantString:  `32`,\n\t}, {\n\t\tdescription: \"uint64\",\n\t\tvalue:       uint64(64),\n\t\twantString:  `64`,\n\t}, {\n\t\tdescription: \"float32\",\n\t\tvalue:       float32(32.32),\n\t\twantString:  `32.31999969482422`,\n\t}, {\n\t\tdescription: \"float64\",\n\t\tvalue:       float64(64.6464),\n\t\twantString:  `64.6464`,\n\t}, {\n\t\tdescription: \"sql.NamedArg\",\n\t\tvalue:       sql.Named(\"test\", 7),\n\t\twantString:  `7`,\n\t}, {\n\t\tdescription: \"sql.NullBool NULL\",\n\t\tvalue:       sql.NullBool{},\n\t\twantString:  `NULL`,\n\t}, {\n\t\tdescription: \"sql.NullBool true\",\n\t\tvalue:       sql.NullBool{Valid: true, Bool: true},\n\t\twantString:  `TRUE`,\n\t}, {\n\t\tdescription: \"sql.NullBool false\",\n\t\tvalue:       sql.NullBool{Valid: true, Bool: false},\n\t\twantString:  `FALSE`,\n\t}, {\n\t\tdescription: \"sqlserver sql.NullBool NULL\",\n\t\tdialect:     DialectSQLServer,\n\t\tvalue:       sql.NullBool{},\n\t\twantString:  `NULL`,\n\t}, {\n\t\tdescription: \"sqlserver sql.NullBool true\",\n\t\tdialect:     DialectSQLServer,\n\t\tvalue:       sql.NullBool{Valid: true, Bool: true},\n\t\twantString:  `1`,\n\t}, {\n\t\tdescription: \"sqlserver sql.NullBool false\",\n\t\tdialect:     DialectSQLServer,\n\t\tvalue:       sql.NullBool{Valid: true, Bool: false},\n\t\twantString:  `0`,\n\t}, {\n\t\tdescription: \"sql.NullFloat64 NULL\",\n\t\tvalue:       sql.NullFloat64{},\n\t\twantString:  `NULL`,\n\t}, {\n\t\tdescription: \"sql.NullFloat64\",\n\t\tvalue:       sql.NullFloat64{Valid: true, Float64: 3.0},\n\t\twantString:  `3`,\n\t}, {\n\t\tdescription: \"sql.NullInt64Field NULL\",\n\t\tvalue:       sql.NullInt64{},\n\t\twantString:  `NULL`,\n\t}, {\n\t\tdescription: \"sql.NullInt64Field\",\n\t\tvalue:       sql.NullInt64{Valid: true, Int64: 5},\n\t\twantString:  `5`,\n\t}, {\n\t\tdescription: \"sql.NullInt32 NULL\",\n\t\tvalue:       sql.NullInt32{},\n\t\twantString:  `NULL`,\n\t}, {\n\t\tdescription: \"sql.NullInt32\",\n\t\tvalue:       sql.NullInt32{Valid: true, Int32: 7},\n\t\twantString:  `7`,\n\t}, {\n\t\tdescription: \"sql.NullStringField NULL\",\n\t\tvalue:       sql.NullString{},\n\t\twantString:  `NULL`,\n\t}, {\n\t\tdescription: \"sql.NullStringField\",\n\t\tvalue:       sql.NullString{Valid: true, String: \"pp\"},\n\t\twantString:  `'pp'`,\n\t}, {\n\t\tdescription: \"sql.NullTimeField NULL\",\n\t\tvalue:       sql.NullTime{},\n\t\twantString:  `NULL`,\n\t}, {\n\t\tdescription: \"sql.NullTime\",\n\t\tvalue: sql.NullTime{\n\t\t\tValid: true,\n\t\t\tTime:  time.Unix(0, 0).UTC(),\n\t\t},\n\t\twantString: `'1970-01-01 00:00:00'`,\n\t}, {\n\t\tdescription: \"sql.NullTime (Postgres)\",\n\t\tdialect:     DialectPostgres,\n\t\tvalue: sql.NullTime{\n\t\t\tValid: true,\n\t\t\tTime:  time.Unix(0, 0).UTC(),\n\t\t},\n\t\twantString: `'1970-01-01 00:00:00+00:00'`,\n\t}, {\n\t\tdescription: \"int64 Valuer\",\n\t\tvalue:       driverValuer{int64(3)},\n\t\twantString:  `3`,\n\t}, {\n\t\tdescription: \"float64 Valuer\",\n\t\tvalue:       driverValuer{64.6464},\n\t\twantString:  `64.6464`,\n\t}, {\n\t\tdescription: \"bool Valuer 1\",\n\t\tvalue:       driverValuer{true},\n\t\twantString:  `TRUE`,\n\t}, {\n\t\tdescription: \"bool Valuer 0\",\n\t\tvalue:       driverValuer{false},\n\t\twantString:  `FALSE`,\n\t}, {\n\t\tdescription: \"bytes Valuer\",\n\t\tvalue:       driverValuer{[]byte{0xab, 0xba}},\n\t\twantString:  `x'abba'`,\n\t}, {\n\t\tdescription: \"string Valuer\",\n\t\tvalue:       driverValuer{`'' ha '; DROP TABLE users; --`},\n\t\twantString:  `''''' ha ''; DROP TABLE users; --'`,\n\t}, {\n\t\tdescription: \"time.Time Valuer\",\n\t\tvalue:       driverValuer{time.Unix(0, 0).UTC()},\n\t\twantString:  `'1970-01-01 00:00:00'`,\n\t}, {\n\t\tdescription: \"time.Time Valuer (Postgres)\",\n\t\tdialect:     DialectPostgres,\n\t\tvalue:       driverValuer{time.Unix(0, 0).UTC()},\n\t\twantString:  `'1970-01-01 00:00:00+00:00'`,\n\t}, {\n\t\tdescription: \"time.Time Valuer (Postgres)\",\n\t\tdialect:     DialectPostgres,\n\t\tvalue:       driverValuer{time.Unix(22, 330000000).In(singaporeLocation)},\n\t\twantString:  `'1970-01-01 07:30:22.33+07:30'`,\n\t}, {\n\t\tdescription: \"string Valuer ptr\",\n\t\tvalue:       &driverValuer{`'' ha '; DROP TABLE users; --`},\n\t\twantString:  `''''' ha ''; DROP TABLE users; --'`,\n\t}, {\n\t\tdescription: \"int ptr\",\n\t\tvalue: func() *int {\n\t\t\tnum := 33\n\t\t\treturn &num\n\t\t}(),\n\t\twantString: `33`,\n\t}, {\n\t\tdescription: \"nil int ptr\",\n\t\tvalue: func() *int {\n\t\t\tvar num *int\n\t\t\treturn num\n\t\t}(),\n\t\twantString: `NULL`,\n\t}, {\n\t\tdescription: \"string ptr\",\n\t\tvalue: func() *string {\n\t\t\tstr := \"test string\"\n\t\t\treturn &str\n\t\t}(),\n\t\twantString: `'test string'`,\n\t}, {\n\t\tdescription: \"nil string ptr\",\n\t\tvalue: func() *string {\n\t\t\tvar str *string\n\t\t\treturn str\n\t\t}(),\n\t\twantString: `NULL`,\n\t}, {\n\t\tdescription: \"sql.NullInt64 ptr\",\n\t\tvalue: &sql.NullInt64{\n\t\t\tValid: true,\n\t\t\tInt64: 33,\n\t\t},\n\t\twantString: `33`,\n\t}, {\n\t\tdescription: \"sql.NullString ptr\",\n\t\tvalue: &sql.NullString{\n\t\t\tValid:  true,\n\t\t\tString: \"test string\",\n\t\t},\n\t\twantString: `'test string'`,\n\t}, {\n\t\tdescription: \"mysql string\",\n\t\tdialect:     DialectMySQL,\n\t\tvalue:       \"the quick brown fox\",\n\t\twantString:  `'the quick brown fox'`,\n\t}, {\n\t\tdescription: \"mysql string newlines in middle\",\n\t\tdialect:     DialectMySQL,\n\t\tvalue:       \"the quick\\nbrown\\r\\nfox\",\n\t\twantString:  `CONCAT('the quick', CHAR(10), 'brown', CHAR(13), CHAR(10), 'fox')`,\n\t}, {\n\t\tdescription: \"mysql string newlines at end\",\n\t\tdialect:     DialectMySQL,\n\t\tvalue:       \"\\nthe quick brown fox\\r\\n\",\n\t\twantString:  `CONCAT(CHAR(10), 'the quick brown fox', CHAR(13), CHAR(10))`,\n\t}, {\n\t\tdescription: \"postgres string\",\n\t\tdialect:     DialectPostgres,\n\t\tvalue:       \"the quick brown fox\",\n\t\twantString:  `'the quick brown fox'`,\n\t}, {\n\t\tdescription: \"postgres string newlines in middle\",\n\t\tdialect:     DialectPostgres,\n\t\tvalue:       \"the quick\\nbrown\\r\\nfox\",\n\t\twantString:  `'the quick' || CHR(10) || 'brown' || CHR(13) || CHR(10) || 'fox'`,\n\t}, {\n\t\tdescription: \"postgres string newlines at end\",\n\t\tdialect:     DialectPostgres,\n\t\tvalue:       \"\\nthe quick brown fox\\r\\n\",\n\t\twantString:  `CHR(10) || 'the quick brown fox' || CHR(13) || CHR(10)`,\n\t}, {\n\t\tdescription: \"sql.NullString with newlines\",\n\t\tdialect:     DialectPostgres,\n\t\tvalue: sql.NullString{\n\t\t\tValid:  true,\n\t\t\tString: \"\\rthe quick\\nbrown fox\\r\\n\",\n\t\t},\n\t\twantString: `CHR(13) || 'the quick' || CHR(10) || 'brown fox' || CHR(13) || CHR(10)`,\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tgotString, err := Sprint(tt.dialect, tt.value)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(gotString, tt.wantString); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype tmptable string\n\nvar _ Table = (*tmptable)(nil)\n\nfunc (t tmptable) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tschema, name := \"\", string(t)\n\tif i := strings.IndexByte(name, '.'); i >= 0 {\n\t\tschema, name = name[:i], name[i+1:]\n\t}\n\tif schema != \"\" {\n\t\tbuf.WriteString(QuoteIdentifier(dialect, schema) + \".\")\n\t}\n\tbuf.WriteString(QuoteIdentifier(dialect, name))\n\treturn nil\n}\n\nfunc (t tmptable) GetAlias() string { return \"\" }\n\nfunc (t tmptable) IsTable() {}\n\ntype tmpfield string\n\nvar _ Field = (*tmpfield)(nil)\n\nfunc (f tmpfield) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\ttable, name := \"\", string(f)\n\tif i := strings.IndexByte(name, '.'); i >= 0 {\n\t\ttable, name = name[:i], name[i+1:]\n\t}\n\tif table != \"\" {\n\t\tbuf.WriteString(QuoteIdentifier(dialect, table) + \".\")\n\t}\n\tbuf.WriteString(QuoteIdentifier(dialect, name))\n\treturn nil\n}\n\nfunc (f tmpfield) WithPrefix(prefix string) Field {\n\tbody := f\n\tif i := strings.IndexByte(string(f), '.'); i >= 0 {\n\t\tbody = f[i+1:]\n\t}\n\tif prefix == \"\" {\n\t\treturn body\n\t}\n\treturn tmpfield(prefix + \".\" + string(body))\n}\n\nfunc (f tmpfield) GetAlias() string { return \"\" }\n\nfunc (f tmpfield) IsField() {}\n\ntype FaultySQLError struct{}\n\nfunc (e FaultySQLError) Error() string { return \"sql broke\" }\n\nvar ErrFaultySQL error = FaultySQLError{}\n\nvar _ interface {\n\tQuery\n\tTable\n\tField\n\tPredicate\n\tAssignment\n} = (*FaultySQL)(nil)\n\ntype FaultySQL struct{}\n\nfunc (q FaultySQL) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn ErrFaultySQL\n}\n\nfunc (q FaultySQL) SetFetchableFields([]Field) (Query, bool) { return nil, false }\n\nfunc (q FaultySQL) GetFetchableFields() ([]Field, bool) { return nil, false }\n\nfunc (q FaultySQL) GetAlias() string { return \"\" }\n\nfunc (q FaultySQL) GetDialect() string { return \"\" }\n\nfunc (q FaultySQL) IsBoolean() {}\n\nfunc (q FaultySQL) IsTable() {}\n\nfunc (q FaultySQL) IsField() {}\n\nfunc (q FaultySQL) IsAssignment() {}\n\ntype driverValuer struct{ value any }\n\nfunc (v driverValuer) Value() (driver.Value, error) { return v.value, nil }\n\ntype dialectValuer struct {\n\tmysqlValuer driver.Valuer\n\tvaluer      driver.Valuer\n}\n\nfunc (v dialectValuer) DialectValuer(dialect string) (driver.Valuer, error) {\n\tif dialect == DialectMySQL {\n\t\treturn v.mysqlValuer, nil\n\t}\n\treturn v.valuer, nil\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/bokwoon95/sq\n\ngo 1.19\n\nrequire (\n\tgithub.com/denisenkom/go-mssqldb v0.12.3\n\tgithub.com/go-sql-driver/mysql v1.7.1\n\tgithub.com/google/go-cmp v0.5.9\n\tgithub.com/google/uuid v1.3.0\n\tgithub.com/lib/pq v1.10.9\n\tgithub.com/mattn/go-sqlite3 v1.14.16\n)\n\nrequire (\n\tgithub.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect\n\tgithub.com/golang-sql/sqlexp v0.1.0 // indirect\n\tgolang.org/x/crypto v0.9.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=\ngithub.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=\ngithub.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=\ngithub.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=\ngithub.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=\ngithub.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=\ngithub.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=\ngithub.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=\ngithub.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=\ngithub.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=\ngithub.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=\ngithub.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=\ngithub.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=\ngithub.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=\ngithub.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=\ngolang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "insert_query.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n)\n\n// InsertQuery represents an SQL INSERT query.\ntype InsertQuery struct {\n\tDialect      string\n\tColumnMapper func(*Column)\n\t// WITH\n\tCTEs []CTE\n\t// INSERT INTO\n\tInsertIgnore  bool\n\tInsertTable   Table\n\tInsertColumns []Field\n\t// VALUES\n\tRowValues []RowValue\n\tRowAlias  string\n\t// SELECT\n\tSelectQuery Query\n\t// ON CONFLICT\n\tConflict ConflictClause\n\t// RETURNING\n\tReturningFields []Field\n}\n\nvar _ Query = (*InsertQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q InsertQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) (err error) {\n\tif q.ColumnMapper != nil {\n\t\tcol := &Column{\n\t\t\tdialect:  q.Dialect,\n\t\t\tisUpdate: false,\n\t\t}\n\t\tdefer mapperFunctionPanicked(&err)\n\t\tq.ColumnMapper(col)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tq.InsertColumns, q.RowValues = col.insertColumns, col.rowValues\n\t}\n\t// WITH\n\tif len(q.CTEs) > 0 {\n\t\tif dialect == DialectMySQL {\n\t\t\treturn fmt.Errorf(\"mysql does not support CTEs with INSERT\")\n\t\t}\n\t\terr = writeCTEs(ctx, dialect, buf, args, params, q.CTEs)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"WITH: %w\", err)\n\t\t}\n\t}\n\t// INSERT INTO\n\tif q.InsertIgnore {\n\t\tif dialect != DialectMySQL {\n\t\t\treturn fmt.Errorf(\"%s does not support INSERT IGNORE\", dialect)\n\t\t}\n\t\tbuf.WriteString(\"INSERT IGNORE INTO \")\n\t} else {\n\t\tbuf.WriteString(\"INSERT INTO \")\n\t}\n\tif q.InsertTable == nil {\n\t\treturn fmt.Errorf(\"no table provided to INSERT\")\n\t}\n\terr = q.InsertTable.WriteSQL(ctx, dialect, buf, args, params)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"INSERT INTO: %w\", err)\n\t}\n\tif alias := getAlias(q.InsertTable); alias != \"\" {\n\t\tif dialect == DialectMySQL || dialect == DialectSQLServer {\n\t\t\treturn fmt.Errorf(\"%s does not allow an alias for the INSERT table\", dialect)\n\t\t}\n\t\tbuf.WriteString(\" AS \" + QuoteIdentifier(dialect, alias))\n\t}\n\t// Columns\n\tif len(q.InsertColumns) > 0 {\n\t\tbuf.WriteString(\" (\")\n\t\terr = writeFieldsWithPrefix(ctx, dialect, buf, args, params, q.InsertColumns, \"\", false)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"INSERT INTO: %w\", err)\n\t\t}\n\t\tbuf.WriteString(\")\")\n\t}\n\t// OUTPUT\n\tif len(q.ReturningFields) > 0 && dialect == DialectSQLServer {\n\t\tbuf.WriteString(\" OUTPUT \")\n\t\tfor i, field := range q.ReturningFields {\n\t\t\tif i > 0 {\n\t\t\t\tbuf.WriteString(\", \")\n\t\t\t}\n\t\t\terr = WriteValue(ctx, dialect, buf, args, params, withPrefix(field, \"INSERTED\"))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif alias := getAlias(field); alias != \"\" {\n\t\t\t\tbuf.WriteString(\" AS \" + QuoteIdentifier(dialect, alias))\n\t\t\t}\n\t\t}\n\t}\n\t// VALUES\n\tif len(q.RowValues) > 0 {\n\t\tbuf.WriteString(\" VALUES \")\n\t\terr = RowValues(q.RowValues).WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"VALUES: %w\", err)\n\t\t}\n\t\tif q.RowAlias != \"\" {\n\t\t\tif dialect != DialectMySQL {\n\t\t\t\treturn fmt.Errorf(\"%s does not support row aliases\", dialect)\n\t\t\t}\n\t\t\tbuf.WriteString(\" AS \" + q.RowAlias)\n\t\t}\n\t} else if q.SelectQuery != nil { // SELECT\n\t\tbuf.WriteString(\" \")\n\t\terr = q.SelectQuery.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"SELECT: %w\", err)\n\t\t}\n\t} else {\n\t\treturn fmt.Errorf(\"InsertQuery missing RowValues and SelectQuery (either one is required)\")\n\t}\n\t// ON CONFLICT\n\terr = q.Conflict.WriteSQL(ctx, dialect, buf, args, params)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// RETURNING\n\tif len(q.ReturningFields) > 0 && dialect != DialectSQLServer {\n\t\tif dialect != DialectPostgres && dialect != DialectSQLite && dialect != DialectMySQL {\n\t\t\treturn fmt.Errorf(\"%s INSERT does not support RETURNING\", dialect)\n\t\t}\n\t\tbuf.WriteString(\" RETURNING \")\n\t\terr = writeFields(ctx, dialect, buf, args, params, q.ReturningFields, true)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"RETURNING: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// InsertInto creates a new InsertQuery.\nfunc InsertInto(table Table) InsertQuery {\n\treturn InsertQuery{InsertTable: table}\n}\n\n// Columns sets the InsertColumns field of the InsertQuery.\nfunc (q InsertQuery) Columns(fields ...Field) InsertQuery {\n\tq.InsertColumns = fields\n\treturn q\n}\n\n// Values sets the RowValues field of the InsertQuery.\nfunc (q InsertQuery) Values(values ...any) InsertQuery {\n\tq.RowValues = append(q.RowValues, values)\n\treturn q\n}\n\n// ColumnValues sets the ColumnMapper field of the InsertQuery.\nfunc (q InsertQuery) ColumnValues(colmapper func(*Column)) InsertQuery {\n\tq.ColumnMapper = colmapper\n\treturn q\n}\n\n// Select sets the SelectQuery field of the InsertQuery.\nfunc (q InsertQuery) Select(query Query) InsertQuery {\n\tq.SelectQuery = query\n\treturn q\n}\n\n// ConflictClause represents an SQL conflict clause e.g. ON CONFLICT DO\n// NOTHING/DO UPDATE or ON DUPLICATE KEY UPDATE.\ntype ConflictClause struct {\n\tConstraintName      string\n\tFields              []Field\n\tPredicate           Predicate\n\tDoNothing           bool\n\tResolution          []Assignment\n\tResolutionPredicate Predicate\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (c ConflictClause) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\tif c.ConstraintName == \"\" && len(c.Fields) == 0 && len(c.Resolution) == 0 && !c.DoNothing {\n\t\treturn nil\n\t}\n\tif dialect != DialectSQLite && dialect != DialectPostgres && dialect != DialectMySQL {\n\t\treturn nil\n\t}\n\tif dialect == DialectMySQL {\n\t\tif len(c.Resolution) > 0 {\n\t\t\tbuf.WriteString(\" ON DUPLICATE KEY UPDATE \")\n\t\t\terr = Assignments(c.Resolution).WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"ON DUPLICATE KEY UPDATE: %w\", err)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tbuf.WriteString(\" ON CONFLICT\")\n\tif c.ConstraintName != \"\" {\n\t\tbuf.WriteString(\" ON CONSTRAINT \" + QuoteIdentifier(dialect, c.ConstraintName))\n\t} else if len(c.Fields) > 0 {\n\t\tbuf.WriteString(\" (\")\n\t\terr = writeFieldsWithPrefix(ctx, dialect, buf, args, params, c.Fields, \"\", false)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"ON CONFLICT: %w\", err)\n\t\t}\n\t\tbuf.WriteString(\")\")\n\t\tif c.Predicate != nil {\n\t\t\tbuf.WriteString(\" WHERE \")\n\t\t\tswitch predicate := c.Predicate.(type) {\n\t\t\tcase VariadicPredicate:\n\t\t\t\tpredicate.Toplevel = true\n\t\t\t\terr = predicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"ON CONFLICT ... WHERE: %w\", err)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\terr = c.Predicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"ON CONFLICT ... WHERE: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif len(c.Resolution) == 0 || c.DoNothing {\n\t\tbuf.WriteString(\" DO NOTHING\")\n\t\treturn nil\n\t}\n\tbuf.WriteString(\" DO UPDATE SET \")\n\terr = Assignments(c.Resolution).WriteSQL(ctx, dialect, buf, args, params)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"DO UPDATE SET: %w\", err)\n\t}\n\tif c.ResolutionPredicate != nil {\n\t\tbuf.WriteString(\" WHERE \")\n\t\tswitch predicate := c.ResolutionPredicate.(type) {\n\t\tcase VariadicPredicate:\n\t\t\tpredicate.Toplevel = true\n\t\t\terr = predicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"DO UPDATE SET ... WHERE: %w\", err)\n\t\t\t}\n\t\tdefault:\n\t\t\terr = c.ResolutionPredicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"DO UPDATE SET ... WHERE: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q InsertQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\tswitch q.Dialect {\n\tcase DialectPostgres, DialectSQLite:\n\t\tif len(q.ReturningFields) == 0 {\n\t\t\tq.ReturningFields = fields\n\t\t\treturn q, true\n\t\t}\n\t\treturn q, false\n\tdefault:\n\t\treturn q, false\n\t}\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q InsertQuery) GetFetchableFields() []Field {\n\tswitch q.Dialect {\n\tcase DialectPostgres, DialectSQLite:\n\t\treturn q.ReturningFields\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// GetDialect implements the Query interface.\nfunc (q InsertQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q InsertQuery) SetDialect(dialect string) InsertQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// SQLiteInsertQuery represents an SQLite INSERT query.\ntype SQLiteInsertQuery InsertQuery\n\nvar _ Query = (*SQLiteInsertQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q SQLiteInsertQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn InsertQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// InsertInto creates a new SQLiteInsertQuery.\nfunc (b sqliteQueryBuilder) InsertInto(table Table) SQLiteInsertQuery {\n\treturn SQLiteInsertQuery{\n\t\tDialect:     DialectSQLite,\n\t\tCTEs:        b.ctes,\n\t\tInsertTable: table,\n\t}\n}\n\n// Columns sets the InsertColumns field of the SQLiteInsertQuery.\nfunc (q SQLiteInsertQuery) Columns(fields ...Field) SQLiteInsertQuery {\n\tq.InsertColumns = fields\n\treturn q\n}\n\n// Values sets the RowValues field of the SQLiteInsertQuery.\nfunc (q SQLiteInsertQuery) Values(values ...any) SQLiteInsertQuery {\n\tq.RowValues = append(q.RowValues, values)\n\treturn q\n}\n\n// ColumnValues sets the ColumnMapper field of the SQLiteInsertQuery.\nfunc (q SQLiteInsertQuery) ColumnValues(colmapper func(*Column)) SQLiteInsertQuery {\n\tq.ColumnMapper = colmapper\n\treturn q\n}\n\n// Select sets the SelectQuery field of the SQLiteInsertQuery.\nfunc (q SQLiteInsertQuery) Select(query Query) SQLiteInsertQuery {\n\tq.SelectQuery = query\n\treturn q\n}\n\ntype sqliteInsertConflict struct{ q *SQLiteInsertQuery }\n\n// OnConflict starts the ON CONFLICT clause of the SQLiteInsertQuery.\nfunc (q SQLiteInsertQuery) OnConflict(fields ...Field) sqliteInsertConflict {\n\tq.Conflict.Fields = fields\n\treturn sqliteInsertConflict{q: &q}\n}\n\n// Where adds predicates to the ON CONFLICT clause of the SQLiteInsertQuery.\nfunc (c sqliteInsertConflict) Where(predicates ...Predicate) sqliteInsertConflict {\n\tc.q.Conflict.Predicate = appendPredicates(c.q.Conflict.Predicate, predicates)\n\treturn c\n}\n\n// DoNothing resolves the ON CONFLICT clause of the SQLiteInsertQuery with DO\n// NOTHING.\nfunc (c sqliteInsertConflict) DoNothing() SQLiteInsertQuery {\n\tc.q.Conflict.DoNothing = true\n\treturn *c.q\n}\n\n// DoUpdateSet resolves the ON CONFLICT CLAUSE of the SQLiteInsertQuery with DO UPDATE SET.\nfunc (c sqliteInsertConflict) DoUpdateSet(assignments ...Assignment) SQLiteInsertQuery {\n\tc.q.Conflict.Resolution = assignments\n\treturn *c.q\n}\n\n// Where adds predicates to the DO UPDATE SET clause of the SQLiteInsertQuery.\nfunc (q SQLiteInsertQuery) Where(predicates ...Predicate) SQLiteInsertQuery {\n\tq.Conflict.ResolutionPredicate = appendPredicates(q.Conflict.ResolutionPredicate, predicates)\n\treturn q\n}\n\n// Returning adds fields to the RETURNING clause of the SQLiteInsertQuery.\nfunc (q SQLiteInsertQuery) Returning(fields ...Field) SQLiteInsertQuery {\n\tq.ReturningFields = append(q.ReturningFields, fields...)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q SQLiteInsertQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn InsertQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q SQLiteInsertQuery) GetFetchableFields() []Field {\n\treturn InsertQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q SQLiteInsertQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect returns the dialect of the query.\nfunc (q SQLiteInsertQuery) SetDialect(dialect string) SQLiteInsertQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// PostgresInsertQuery represents a Postgres INSERT query.\ntype PostgresInsertQuery InsertQuery\n\nvar _ Query = (*PostgresInsertQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q PostgresInsertQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn InsertQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// InsertInto creates a new PostgresInsertQuery.\nfunc (b postgresQueryBuilder) InsertInto(table Table) PostgresInsertQuery {\n\treturn PostgresInsertQuery{\n\t\tDialect:     DialectPostgres,\n\t\tCTEs:        b.ctes,\n\t\tInsertTable: table,\n\t}\n}\n\n// Columns sets the InsertColumns field of the PostgresInsertQuery.\nfunc (q PostgresInsertQuery) Columns(fields ...Field) PostgresInsertQuery {\n\tq.InsertColumns = fields\n\treturn q\n}\n\n// Values sets the RowValues field of the PostgresInsertQuery.\nfunc (q PostgresInsertQuery) Values(values ...any) PostgresInsertQuery {\n\tq.RowValues = append(q.RowValues, values)\n\treturn q\n}\n\n// ColumnValues sets the ColumnMapper field of the PostgresInsertQuery.\nfunc (q PostgresInsertQuery) ColumnValues(colmapper func(*Column)) PostgresInsertQuery {\n\tq.ColumnMapper = colmapper\n\treturn q\n}\n\n// Select sets the SelectQuery field of the PostgresInsertQuery.\nfunc (q PostgresInsertQuery) Select(query Query) PostgresInsertQuery {\n\tq.SelectQuery = query\n\treturn q\n}\n\ntype postgresInsertConflict struct{ q *PostgresInsertQuery }\n\n// OnConflict starts the ON CONFLICT clause of the PostgresInsertQuery.\nfunc (q PostgresInsertQuery) OnConflict(fields ...Field) postgresInsertConflict {\n\tq.Conflict.Fields = fields\n\treturn postgresInsertConflict{q: &q}\n}\n\n// OnConflict starts the ON CONFLICT clause of the PostgresInsertQuery.\nfunc (q PostgresInsertQuery) OnConflictOnConstraint(constraintName string) postgresInsertConflict {\n\tq.Conflict.ConstraintName = constraintName\n\treturn postgresInsertConflict{q: &q}\n}\n\n// Where adds predicates to the ON CONFLICT clause of the PostgresInsertQuery.\nfunc (c postgresInsertConflict) Where(predicates ...Predicate) postgresInsertConflict {\n\tc.q.Conflict.Predicate = appendPredicates(c.q.Conflict.Predicate, predicates)\n\treturn c\n}\n\n// DoNothing resolves the ON CONFLICT clause of the PostgresInsertQuery with DO\n// NOTHING.\nfunc (c postgresInsertConflict) DoNothing() PostgresInsertQuery {\n\tc.q.Conflict.DoNothing = true\n\treturn *c.q\n}\n\n// DoUpdateSet resolves the ON CONFLICT CLAUSE of the PostgresInsertQuery with DO UPDATE SET.\nfunc (c postgresInsertConflict) DoUpdateSet(assignments ...Assignment) PostgresInsertQuery {\n\tc.q.Conflict.Resolution = assignments\n\treturn *c.q\n}\n\n// Where adds predicates to the DO UPDATE SET clause of the PostgresInsertQuery.\nfunc (q PostgresInsertQuery) Where(predicates ...Predicate) PostgresInsertQuery {\n\tq.Conflict.ResolutionPredicate = appendPredicates(q.Conflict.ResolutionPredicate, predicates)\n\treturn q\n}\n\n// Returning adds fields to the RETURNING clause of the PostgresInsertQuery.\nfunc (q PostgresInsertQuery) Returning(fields ...Field) PostgresInsertQuery {\n\tq.ReturningFields = append(q.ReturningFields, fields...)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q PostgresInsertQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn InsertQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q PostgresInsertQuery) GetFetchableFields() []Field {\n\treturn InsertQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q PostgresInsertQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect returns the dialect of the query.\nfunc (q PostgresInsertQuery) SetDialect(dialect string) PostgresInsertQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// MySQLInsertQuery represents a MySQL INSERT query.\ntype MySQLInsertQuery InsertQuery\n\nvar _ Query = (*MySQLInsertQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q MySQLInsertQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn InsertQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// InsertInto creates a new MySQLInsertQuery.\nfunc (b mysqlQueryBuilder) InsertInto(table Table) MySQLInsertQuery {\n\treturn MySQLInsertQuery{\n\t\tDialect:     DialectMySQL,\n\t\tCTEs:        b.ctes,\n\t\tInsertTable: table,\n\t}\n}\n\n// InsertInto creates a new MySQLInsertQuery.\nfunc (b mysqlQueryBuilder) InsertIgnoreInto(table Table) MySQLInsertQuery {\n\treturn MySQLInsertQuery{\n\t\tDialect:      DialectMySQL,\n\t\tCTEs:         b.ctes,\n\t\tInsertTable:  table,\n\t\tInsertIgnore: true,\n\t}\n}\n\n// Columns sets the InsertColumns field of the MySQLInsertQuery.\nfunc (q MySQLInsertQuery) Columns(fields ...Field) MySQLInsertQuery {\n\tq.InsertColumns = fields\n\treturn q\n}\n\n// Values sets the RowValues field of the MySQLInsertQuery.\nfunc (q MySQLInsertQuery) Values(values ...any) MySQLInsertQuery {\n\tq.RowValues = append(q.RowValues, values)\n\treturn q\n}\n\n// As sets the RowAlias field of the MySQLInsertQuery.\nfunc (q MySQLInsertQuery) As(rowAlias string) MySQLInsertQuery {\n\tq.RowAlias = rowAlias\n\treturn q\n}\n\n// ColumnValues sets the ColumnMapper field of the MySQLInsertQuery.\nfunc (q MySQLInsertQuery) ColumnValues(colmapper func(*Column)) MySQLInsertQuery {\n\tq.ColumnMapper = colmapper\n\treturn q\n}\n\n// Select sets the SelectQuery field of the MySQLInsertQuery.\nfunc (q MySQLInsertQuery) Select(query Query) MySQLInsertQuery {\n\tq.SelectQuery = query\n\treturn q\n}\n\n// OnDuplicateKeyUpdate sets the ON DUPLICATE KEY UPDATE clause of the\n// MySQLInsertQuery.\nfunc (q MySQLInsertQuery) OnDuplicateKeyUpdate(assignments ...Assignment) MySQLInsertQuery {\n\tq.Conflict.Resolution = assignments\n\treturn q\n}\n\n// Returning adds fields to the RETURNING clause of the MySQLInsertQuery.\nfunc (q MySQLInsertQuery) Returning(fields ...Field) MySQLInsertQuery {\n\tq.ReturningFields = append(q.ReturningFields, fields...)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q MySQLInsertQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn InsertQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q MySQLInsertQuery) GetFetchableFields() []Field {\n\treturn InsertQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q MySQLInsertQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect returns the dialect of the query.\nfunc (q MySQLInsertQuery) SetDialect(dialect string) MySQLInsertQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// SQLServerInsertQuery represents an SQL Server INSERT query.\ntype SQLServerInsertQuery InsertQuery\n\nvar _ Query = (*SQLServerInsertQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q SQLServerInsertQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn InsertQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// InsertInto creates a new SQLServerInsertQuery.\nfunc (b sqlserverQueryBuilder) InsertInto(table Table) SQLServerInsertQuery {\n\treturn SQLServerInsertQuery{\n\t\tDialect:     DialectSQLServer,\n\t\tCTEs:        b.ctes,\n\t\tInsertTable: table,\n\t}\n}\n\n// Columns sets the InsertColumns field of the SQLServerInsertQuery.\nfunc (q SQLServerInsertQuery) Columns(fields ...Field) SQLServerInsertQuery {\n\tq.InsertColumns = fields\n\treturn q\n}\n\n// Values sets the RowValues field of the SQLServerInsertQuery.\nfunc (q SQLServerInsertQuery) Values(values ...any) SQLServerInsertQuery {\n\tq.RowValues = append(q.RowValues, values)\n\treturn q\n}\n\n// ColumnValues sets the ColumnMapper field of the SQLServerInsertQuery.\nfunc (q SQLServerInsertQuery) ColumnValues(colmapper func(*Column)) SQLServerInsertQuery {\n\tq.ColumnMapper = colmapper\n\treturn q\n}\n\n// Select sets the SelectQuery field of the SQLServerInsertQuery.\nfunc (q SQLServerInsertQuery) Select(query Query) SQLServerInsertQuery {\n\tq.SelectQuery = query\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q SQLServerInsertQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn InsertQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q SQLServerInsertQuery) GetFetchableFields() []Field {\n\treturn InsertQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q SQLServerInsertQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect returns the dialect of the query.\nfunc (q SQLServerInsertQuery) SetDialect(dialect string) SQLServerInsertQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n"
  },
  {
    "path": "insert_query_test.go",
    "content": "package sq\n\nimport (\n\t\"testing\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n)\n\nfunc TestSQLiteInsertQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := SQLite.InsertInto(a).Returning(a.FIRST_NAME).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectSQLite)\n\t\tfields := q1.GetFetchableFields()\n\t\tif diff := testutil.Diff(fields, []Field{a.FIRST_NAME}); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Columns Values\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLite.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tValues(\"bob\", \"the builder\").\n\t\t\tValues(\"alice\", \"in wonderland\")\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor AS a (first_name, last_name)\" +\n\t\t\t\" VALUES ($1, $2), ($3, $4)\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"ColumnValues\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLite.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumnValues(func(col *Column) {\n\t\t\t\t// bob\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"bob\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"the builder\")\n\t\t\t\t// alice\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"alice\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"in wonderland\")\n\t\t\t})\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor AS a (first_name, last_name)\" +\n\t\t\t\" VALUES ($1, $2), ($3, $4)\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"Insert Returning\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLite.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelect(SQLite.Select(a.FIRST_NAME, a.LAST_NAME).From(a)).\n\t\t\tReturning(a.ACTOR_ID)\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor AS a (first_name, last_name)\" +\n\t\t\t\" SELECT a.first_name, a.last_name FROM actor AS a\" +\n\t\t\t\" RETURNING a.actor_id\"\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"OnConflict DoNothing\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLite.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tValues(\"bob\", \"the builder\").\n\t\t\tValues(\"alice\", \"in wonderland\").\n\t\t\tOnConflict(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tWhere(And(a.FIRST_NAME.IsNotNull(), a.LAST_NAME.IsNotNull())).\n\t\t\tDoNothing()\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor AS a (first_name, last_name)\" +\n\t\t\t\" VALUES ($1, $2), ($3, $4)\" +\n\t\t\t\" ON CONFLICT (first_name, last_name)\" +\n\t\t\t\" WHERE a.first_name IS NOT NULL AND a.last_name IS NOT NULL\" +\n\t\t\t\" DO NOTHING\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"OnConflict DoUpdateSet\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLite.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tValues(\"bob\", \"the builder\").\n\t\t\tValues(\"alice\", \"in wonderland\").\n\t\t\tOnConflict(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tDoUpdateSet(\n\t\t\t\ta.FIRST_NAME.Set(a.FIRST_NAME.WithPrefix(\"EXCLUDED\")),\n\t\t\t\ta.LAST_NAME.Set(a.LAST_NAME.WithPrefix(\"EXCLUDED\")),\n\t\t\t).\n\t\t\tWhere(And(a.FIRST_NAME.IsNotNull(), a.LAST_NAME.IsNotNull()))\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor AS a (first_name, last_name)\" +\n\t\t\t\" VALUES ($1, $2), ($3, $4)\" +\n\t\t\t\" ON CONFLICT (first_name, last_name)\" +\n\t\t\t\" DO UPDATE SET first_name = EXCLUDED.first_name, last_name = EXCLUDED.last_name\" +\n\t\t\t\" WHERE a.first_name IS NOT NULL AND a.last_name IS NOT NULL\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestPostgresInsertQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := Postgres.InsertInto(a).Returning(a.FIRST_NAME).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectPostgres)\n\t\tfields := q1.GetFetchableFields()\n\t\tif diff := testutil.Diff(fields, []Field{a.FIRST_NAME}); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Columns Values\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tValues(\"bob\", \"the builder\").\n\t\t\tValues(\"alice\", \"in wonderland\")\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor AS a (first_name, last_name)\" +\n\t\t\t\" VALUES ($1, $2), ($3, $4)\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"ColumnValues\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumnValues(func(col *Column) {\n\t\t\t\t// bob\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"bob\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"the builder\")\n\t\t\t\t// alice\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"alice\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"in wonderland\")\n\t\t\t})\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor AS a (first_name, last_name)\" +\n\t\t\t\" VALUES ($1, $2), ($3, $4)\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"Insert Returning\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelect(Postgres.Select(a.FIRST_NAME, a.LAST_NAME).From(a)).\n\t\t\tReturning(a.ACTOR_ID)\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor AS a (first_name, last_name)\" +\n\t\t\t\" SELECT a.first_name, a.last_name FROM actor AS a\" +\n\t\t\t\" RETURNING a.actor_id\"\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"OnConflict DoNothing\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tValues(\"bob\", \"the builder\").\n\t\t\tValues(\"alice\", \"in wonderland\").\n\t\t\tOnConflict(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tWhere(And(a.FIRST_NAME.IsNotNull(), a.LAST_NAME.IsNotNull())).\n\t\t\tDoNothing()\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor AS a (first_name, last_name)\" +\n\t\t\t\" VALUES ($1, $2), ($3, $4)\" +\n\t\t\t\" ON CONFLICT (first_name, last_name)\" +\n\t\t\t\" WHERE a.first_name IS NOT NULL AND a.last_name IS NOT NULL\" +\n\t\t\t\" DO NOTHING\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"OnConflictOnConstraint DoUpdateSet\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tValues(\"bob\", \"the builder\").\n\t\t\tValues(\"alice\", \"in wonderland\").\n\t\t\tOnConflictOnConstraint(\"actor_first_name_last_name_key\").\n\t\t\tDoUpdateSet(\n\t\t\t\ta.FIRST_NAME.Set(a.FIRST_NAME.WithPrefix(\"EXCLUDED\")),\n\t\t\t\ta.LAST_NAME.Set(a.LAST_NAME.WithPrefix(\"EXCLUDED\")),\n\t\t\t).\n\t\t\tWhere(And(a.FIRST_NAME.IsNotNull(), a.LAST_NAME.IsNotNull()))\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor AS a (first_name, last_name)\" +\n\t\t\t\" VALUES ($1, $2), ($3, $4)\" +\n\t\t\t\" ON CONFLICT ON CONSTRAINT actor_first_name_last_name_key\" +\n\t\t\t\" DO UPDATE SET first_name = EXCLUDED.first_name, last_name = EXCLUDED.last_name\" +\n\t\t\t\" WHERE a.first_name IS NOT NULL AND a.last_name IS NOT NULL\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestMySQLInsertQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := MySQL.InsertInto(a).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectMySQL)\n\t\tfields := q1.GetFetchableFields()\n\t\tif len(fields) != 0 {\n\t\t\tt.Error(testutil.Callers(), \"expected 0 fields but got %v\", fields)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Columns Values\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tValues(\"bob\", \"the builder\").\n\t\t\tValues(\"alice\", \"in wonderland\")\n\t\ttt.wantQuery = \"INSERT INTO actor (first_name, last_name)\" +\n\t\t\t\" VALUES (?, ?), (?, ?)\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"ColumnValues\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tInsertInto(a).\n\t\t\tColumnValues(func(col *Column) {\n\t\t\t\t// bob\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"bob\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"the builder\")\n\t\t\t\t// alice\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"alice\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"in wonderland\")\n\t\t\t})\n\t\ttt.wantQuery = \"INSERT INTO actor (first_name, last_name)\" +\n\t\t\t\" VALUES (?, ?), (?, ?)\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"Insert Returning\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelect(SQLite.Select(a.FIRST_NAME, a.LAST_NAME).From(a)).\n\t\t\tReturning(a.ACTOR_ID)\n\t\ttt.wantQuery = \"INSERT INTO actor (first_name, last_name)\" +\n\t\t\t\" SELECT actor.first_name, actor.last_name FROM actor\" +\n\t\t\t\" RETURNING actor.actor_id\"\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"Select InsertIgnore\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tInsertIgnoreInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelect(MySQL.Select(a.FIRST_NAME, a.LAST_NAME).From(a))\n\t\ttt.wantQuery = \"INSERT IGNORE INTO actor (first_name, last_name)\" +\n\t\t\t\" SELECT actor.first_name, actor.last_name FROM actor\"\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"OnDuplicateKey DoNothing\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tValues(\"bob\", \"the builder\").\n\t\t\tValues(\"alice\", \"in wonderland\").\n\t\t\tOnDuplicateKeyUpdate(a.FIRST_NAME.Set(a.FIRST_NAME), a.LAST_NAME.Set(a.LAST_NAME))\n\t\ttt.wantQuery = \"INSERT INTO actor (first_name, last_name)\" +\n\t\t\t\" VALUES (?, ?), (?, ?)\" +\n\t\t\t\" ON DUPLICATE KEY UPDATE actor.first_name = actor.first_name, actor.last_name = actor.last_name\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"OnDuplicateKey\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tValues(\"bob\", \"the builder\").\n\t\t\tValues(\"alice\", \"in wonderland\").\n\t\t\tAs(\"new\").\n\t\t\tOnDuplicateKeyUpdate(\n\t\t\t\ta.FIRST_NAME.Set(a.FIRST_NAME.WithPrefix(\"new\")),\n\t\t\t\ta.LAST_NAME.Set(a.LAST_NAME.WithPrefix(\"new\")),\n\t\t\t)\n\t\ttt.wantQuery = \"INSERT INTO actor (first_name, last_name)\" +\n\t\t\t\" VALUES (?, ?), (?, ?) AS new\" +\n\t\t\t\" ON DUPLICATE KEY UPDATE actor.first_name = new.first_name, actor.last_name = new.last_name\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestSQLServerInsertQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := SQLServer.InsertInto(a).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectSQLServer)\n\t\tfields := q1.GetFetchableFields()\n\t\tif len(fields) != 0 {\n\t\t\tt.Error(testutil.Callers(), \"expected 0 fields but got %v\", fields)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Columns Values\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLServer.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tValues(\"bob\", \"the builder\").\n\t\t\tValues(\"alice\", \"in wonderland\")\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor (first_name, last_name)\" +\n\t\t\t\" VALUES (@p1, @p2), (@p3, @p4)\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"ColumnValues\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLServer.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumnValues(func(col *Column) {\n\t\t\t\t// bob\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"bob\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"the builder\")\n\t\t\t\t// alice\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"alice\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"in wonderland\")\n\t\t\t})\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor (first_name, last_name)\" +\n\t\t\t\" VALUES (@p1, @p2), (@p3, @p4)\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", \"alice\", \"in wonderland\"}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"Select\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLServer.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tInsertInto(a).\n\t\t\tColumns(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelect(SQLServer.Select(a.FIRST_NAME, a.LAST_NAME).From(a))\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" INSERT INTO actor (first_name, last_name)\" +\n\t\t\t\" SELECT actor.first_name, actor.last_name FROM actor\"\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestInsertQuery(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := InsertQuery{InsertTable: Expr(\"tbl\"), Dialect: \"lorem ipsum\"}\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tf1, f2, f3 := Expr(\"f1\"), Expr(\"f2\"), Expr(\"f3\")\n\tcolmapper := func(col *Column) {\n\t\tcol.Set(f1, 1)\n\t\tcol.Set(f2, 2)\n\t\tcol.Set(f3, 3)\n\t}\n\n\tnotOKTests := []TestTable{{\n\t\tdescription: \"mysql does not support CTEs with INSERT\",\n\t\titem: InsertQuery{\n\t\t\tDialect:      DialectMySQL,\n\t\t\tCTEs:         []CTE{NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))},\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"dialect does not support INSERT IGNORE\",\n\t\titem: InsertQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tInsertIgnore: true,\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"nil IntoTable not allowed\",\n\t\titem: InsertQuery{\n\t\t\tInsertTable:  nil,\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"dialect does not support IntoTable alias\",\n\t\titem: InsertQuery{\n\t\t\tDialect:      DialectMySQL,\n\t\t\tInsertTable:  Expr(\"tbl\").As(\"t\"),\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"nil Field in InsertColumns not allowed\",\n\t\titem: InsertQuery{\n\t\t\tDialect:       DialectMySQL,\n\t\t\tInsertTable:   Expr(\"tbl\"),\n\t\t\tInsertColumns: Fields{nil},\n\t\t\tRowValues:     RowValues{{1}},\n\t\t},\n\t}, {\n\t\tdescription: \"dialect does not support row alias\",\n\t\titem: InsertQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tRowAlias:     \"new\",\n\t\t},\n\t}, {\n\t\tdescription: \"missing both Values and Select not allowed (either one is required)\",\n\t\titem: InsertQuery{\n\t\t\tInsertTable: Expr(\"tbl\"),\n\t\t},\n\t}, {\n\t\tdescription: \"missing both Values and Select not allowed (either one is required)\",\n\t\titem: InsertQuery{\n\t\t\tInsertTable: Expr(\"tbl\"),\n\t\t},\n\t}, {\n\t\tdescription: \"nil Field in ConflictFields not allowed\",\n\t\titem: InsertQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tConflict:     ConflictClause{Fields: Fields{nil}},\n\t\t},\n\t}}\n\n\tfor _, tt := range notOKTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertNotOK(t)\n\t\t})\n\t}\n\n\terrTests := []TestTable{{\n\t\tdescription: \"ColumnMapper err\",\n\t\titem: InsertQuery{\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: func(*Column) { panic(ErrFaultySQL) },\n\t\t},\n\t}, {\n\t\tdescription: \"CTEs err\",\n\t\titem: InsertQuery{\n\t\t\tCTEs:         []CTE{NewCTE(\"cte\", nil, FaultySQL{})},\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"IntoTable err\",\n\t\titem: InsertQuery{\n\t\t\tInsertTable:  FaultySQL{},\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"RowValues err\",\n\t\titem: InsertQuery{\n\t\t\tInsertTable:   Expr(\"tbl\"),\n\t\t\tInsertColumns: Fields{f1, f2, f3},\n\t\t\tRowValues:     RowValues{{1, 2, FaultySQL{}}},\n\t\t},\n\t}, {\n\t\tdescription: \"SelectQuery err\",\n\t\titem: InsertQuery{\n\t\t\tInsertTable:   Expr(\"tbl\"),\n\t\t\tInsertColumns: Fields{f1, f2, f3},\n\t\t\tSelectQuery:   Queryf(\"SELECT 1, 2, {}\", FaultySQL{}),\n\t\t},\n\t}, {\n\t\tdescription: \"ConflictPredicate VariadicPredicate err\",\n\t\titem: InsertQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tConflict: ConflictClause{\n\t\t\t\tFields:    Fields{f1, f2},\n\t\t\t\tPredicate: And(FaultySQL{}),\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"ConflictPredicate err\",\n\t\titem: InsertQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tConflict: ConflictClause{\n\t\t\t\tFields:    Fields{f1, f2},\n\t\t\t\tPredicate: FaultySQL{},\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"Resolution err\",\n\t\titem: InsertQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tConflict: ConflictClause{\n\t\t\t\tFields:     Fields{f1, f2},\n\t\t\t\tResolution: Assignments{FaultySQL{}},\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"ResolutionPredicate VariadicPredicate err\",\n\t\titem: InsertQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tConflict: ConflictClause{\n\t\t\t\tFields:              Fields{f1, f2},\n\t\t\t\tResolution:          Assignments{FaultySQL{}},\n\t\t\t\tResolutionPredicate: And(FaultySQL{}),\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"ResolutionPredicate err\",\n\t\titem: InsertQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tConflict: ConflictClause{\n\t\t\t\tFields:              Fields{f1, f2},\n\t\t\t\tResolution:          Assignments{Set(f1, f1)},\n\t\t\t\tResolutionPredicate: FaultySQL{},\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"mysql Resolution err\",\n\t\titem: InsertQuery{\n\t\t\tDialect:      DialectMySQL,\n\t\t\tInsertTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tConflict: ConflictClause{\n\t\t\t\tResolution: Assignments{Set(f1, FaultySQL{})},\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"ReturningFields err\",\n\t\titem: InsertQuery{\n\t\t\tDialect:         DialectPostgres,\n\t\t\tInsertTable:     Expr(\"tbl\"),\n\t\t\tColumnMapper:    colmapper,\n\t\t\tReturningFields: Fields{FaultySQL{}},\n\t\t},\n\t}}\n\n\tfor _, tt := range errTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertErr(t, ErrFaultySQL)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "integration_test.go",
    "content": "package sq\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n\t_ \"github.com/denisenkom/go-mssqldb\"\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/google/uuid\"\n\t_ \"github.com/lib/pq\"\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\ntype Color int\n\nconst (\n\tColorInvalid Color = iota\n\tColorRed\n\tColorGreen\n\tColorBlue\n)\n\nvar colorNames = [...]string{\n\tColorInvalid: \"\",\n\tColorRed:     \"red\",\n\tColorGreen:   \"green\",\n\tColorBlue:    \"blue\",\n}\n\nfunc (c Color) Enumerate() []string { return colorNames[:] }\n\ntype Direction string\n\nconst (\n\tDirectionInvalid = Direction(\"\")\n\tDirectionNorth   = Direction(\"north\")\n\tDirectionSouth   = Direction(\"south\")\n\tDirectionEast    = Direction(\"east\")\n\tDirectionWest    = Direction(\"west\")\n)\n\nfunc (d Direction) Enumerate() []string {\n\treturn []string{\n\t\tstring(DirectionInvalid),\n\t\tstring(DirectionNorth),\n\t\tstring(DirectionSouth),\n\t\tstring(DirectionEast),\n\t\tstring(DirectionWest),\n\t}\n}\n\nfunc TestRow(t *testing.T) {\n\ttype TestTable struct {\n\t\tdialect  string\n\t\tdriver   string\n\t\tdsn      string\n\t\tteardown string\n\t\tsetup    string\n\t}\n\n\ttests := []TestTable{{\n\t\tdialect:  DialectSQLite,\n\t\tdriver:   \"sqlite3\",\n\t\tdsn:      \"file:/TestRow/sqlite?vfs=memdb&_foreign_keys=true\",\n\t\tteardown: \"DROP TABLE IF EXISTS table00;\",\n\t\tsetup: \"CREATE TABLE table00 (\" +\n\t\t\t\"\\n    uuid UUID\" +\n\t\t\t\"\\n    ,data JSON\" +\n\t\t\t\"\\n    ,color TEXT\" +\n\t\t\t\"\\n    ,direction TEXT\" +\n\t\t\t\"\\n    ,weekday TEXT\" +\n\t\t\t\"\\n    ,text_array JSON\" +\n\t\t\t\"\\n    ,int_array JSON\" +\n\t\t\t\"\\n    ,int64_array JSON\" +\n\t\t\t\"\\n    ,int32_array JSON\" +\n\t\t\t\"\\n    ,float64_array JSON\" +\n\t\t\t\"\\n    ,float32_array JSON\" +\n\t\t\t\"\\n    ,bool_array JSON\" +\n\t\t\t\"\\n    ,bytes BLOB\" +\n\t\t\t\"\\n    ,is_active BOOLEAN\" +\n\t\t\t\"\\n    ,price REAL\" +\n\t\t\t\"\\n    ,score BIGINT\" +\n\t\t\t\"\\n    ,name TEXT\" +\n\t\t\t\"\\n    ,updated_at DATETIME\" +\n\t\t\t\"\\n);\",\n\t}, {\n\t\tdialect: DialectPostgres,\n\t\tdriver:  \"postgres\",\n\t\tdsn:     *postgresDSN,\n\t\tteardown: \"DROP TABLE IF EXISTS table00;\" +\n\t\t\t\"\\nDROP TYPE IF EXISTS direction;\" +\n\t\t\t\"\\nDROP TYPE IF EXISTS color;\" +\n\t\t\t\"\\nDROP TYPE IF EXISTS weekday;\",\n\t\tsetup: \"CREATE TYPE color AS ENUM ('red', 'green', 'blue');\" +\n\t\t\t\"\\nCREATE TYPE direction AS ENUM ('north', 'south', 'east', 'west');\" +\n\t\t\t\"\\nCREATE TYPE weekday AS ENUM ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');\" +\n\t\t\t\"\\nCREATE TABLE table00 (\" +\n\t\t\t\"\\n    uuid UUID\" +\n\t\t\t\"\\n    ,data JSONB\" +\n\t\t\t\"\\n    ,color color\" +\n\t\t\t\"\\n    ,direction direction\" +\n\t\t\t\"\\n    ,weekday weekday\" +\n\t\t\t\"\\n    ,text_array TEXT[]\" +\n\t\t\t\"\\n    ,int_array BIGINT[]\" +\n\t\t\t\"\\n    ,int64_array BIGINT[]\" +\n\t\t\t\"\\n    ,int32_array INT[]\" +\n\t\t\t\"\\n    ,float64_array DOUBLE PRECISION[]\" +\n\t\t\t\"\\n    ,float32_array REAL[]\" +\n\t\t\t\"\\n    ,bool_array BOOLEAN[]\" +\n\t\t\t\"\\n    ,is_active BOOLEAN\" +\n\t\t\t\"\\n    ,bytes BYTEA\" +\n\t\t\t\"\\n    ,price DOUBLE PRECISION\" +\n\t\t\t\"\\n    ,score BIGINT\" +\n\t\t\t\"\\n    ,name TEXT\" +\n\t\t\t\"\\n    ,updated_at TIMESTAMPTZ\" +\n\t\t\t\"\\n);\",\n\t}, {\n\t\tdialect:  DialectMySQL,\n\t\tdriver:   \"mysql\",\n\t\tdsn:      *mysqlDSN,\n\t\tteardown: \"DROP TABLE IF EXISTS table00;\",\n\t\tsetup: \"CREATE TABLE table00 (\" +\n\t\t\t\"\\n    uuid BINARY(16)\" +\n\t\t\t\"\\n    ,data JSON\" +\n\t\t\t\"\\n    ,color VARCHAR(255)\" +\n\t\t\t\"\\n    ,direction VARCHAR(255)\" +\n\t\t\t\"\\n    ,weekday VARCHAR(255)\" +\n\t\t\t\"\\n    ,text_array JSON\" +\n\t\t\t\"\\n    ,int_array JSON\" +\n\t\t\t\"\\n    ,int64_array JSON\" +\n\t\t\t\"\\n    ,int32_array JSON\" +\n\t\t\t\"\\n    ,float64_array JSON\" +\n\t\t\t\"\\n    ,float32_array JSON\" +\n\t\t\t\"\\n    ,bool_array JSON\" +\n\t\t\t\"\\n    ,is_active BOOLEAN\" +\n\t\t\t\"\\n    ,bytes LONGBLOB\" +\n\t\t\t\"\\n    ,price DOUBLE PRECISION\" +\n\t\t\t\"\\n    ,score BIGINT\" +\n\t\t\t\"\\n    ,name TEXT\" +\n\t\t\t\"\\n    ,updated_at DATETIME\" +\n\t\t\t\"\\n);\",\n\t}, {\n\t\tdialect:  DialectSQLServer,\n\t\tdriver:   \"sqlserver\",\n\t\tdsn:      *sqlserverDSN,\n\t\tteardown: \"DROP TABLE IF EXISTS table00;\",\n\t\tsetup: \"CREATE TABLE table00 (\" +\n\t\t\t\"\\n    uuid BINARY(16)\" +\n\t\t\t\"\\n    ,data NVARCHAR(MAX)\" +\n\t\t\t\"\\n    ,color NVARCHAR(255)\" +\n\t\t\t\"\\n    ,direction NVARCHAR(255)\" +\n\t\t\t\"\\n    ,weekday NVARCHAR(255)\" +\n\t\t\t\"\\n    ,text_array NVARCHAR(MAX)\" +\n\t\t\t\"\\n    ,int_array NVARCHAR(MAX)\" +\n\t\t\t\"\\n    ,int64_array NVARCHAR(MAX)\" +\n\t\t\t\"\\n    ,int32_array NVARCHAR(MAX)\" +\n\t\t\t\"\\n    ,float64_array NVARCHAR(MAX)\" +\n\t\t\t\"\\n    ,float32_array NVARCHAR(MAX)\" +\n\t\t\t\"\\n    ,bool_array NVARCHAR(MAX)\" +\n\t\t\t\"\\n    ,is_active BIT\" +\n\t\t\t\"\\n    ,bytes VARBINARY(MAX)\" +\n\t\t\t\"\\n    ,price DOUBLE PRECISION\" +\n\t\t\t\"\\n    ,score BIGINT\" +\n\t\t\t\"\\n    ,name NVARCHAR(255)\" +\n\t\t\t\"\\n    ,updated_at DATETIME\" +\n\t\t\t\"\\n);\",\n\t}}\n\n\tvar TABLE00 = New[struct {\n\t\tTableStruct   `sq:\"table00\"`\n\t\tUUID          UUIDField\n\t\tDATA          JSONField\n\t\tCOLOR         EnumField\n\t\tDIRECTION     EnumField\n\t\tWEEKDAY       EnumField\n\t\tTEXT_ARRAY    ArrayField\n\t\tINT_ARRAY     ArrayField\n\t\tINT64_ARRAY   ArrayField\n\t\tINT32_ARRAY   ArrayField\n\t\tFLOAT64_ARRAY ArrayField\n\t\tFLOAT32_ARRAY ArrayField\n\t\tBOOL_ARRAY    ArrayField\n\t\tBYTES         BinaryField\n\t\tIS_ACTIVE     BooleanField\n\t\tPRICE         NumberField\n\t\tSCORE         NumberField\n\t\tNAME          StringField\n\t\tUPDATED_AT    TimeField\n\t}](\"\")\n\n\ttype Table00 struct {\n\t\tuuid         uuid.UUID\n\t\tdata         any\n\t\tcolor        Color\n\t\tdirection    Direction\n\t\tweekday      Weekday\n\t\ttextArray    []string\n\t\tintArray     []int\n\t\tint64Array   []int64\n\t\tint32Array   []int32\n\t\tfloat64Array []float64\n\t\tfloat32Array []float32\n\t\tboolArray    []bool\n\t\tbytes        []byte\n\t\tisActive     bool\n\t\tprice        float64\n\t\tscore        int64\n\t\tname         string\n\t\tupdatedAt    time.Time\n\t}\n\n\tvar table00Values = []Table00{{\n\t\tuuid:         uuid.UUID([16]byte{15: 1}),\n\t\tdata:         map[string]any{\"lorem ipsum\": \"dolor sit amet\"},\n\t\tcolor:        ColorRed,\n\t\tdirection:    DirectionNorth,\n\t\tweekday:      Monday,\n\t\ttextArray:    []string{\"one\", \"two\", \"three\"},\n\t\tintArray:     []int{1, 2, 3},\n\t\tint64Array:   []int64{1, 2, 3},\n\t\tint32Array:   []int32{1, 2, 3},\n\t\tfloat64Array: []float64{1, 2, 3},\n\t\tfloat32Array: []float32{1, 2, 3},\n\t\tboolArray:    []bool{true, false, false},\n\t\tbytes:        []byte{1, 2, 3},\n\t\tisActive:     true,\n\t\tprice:        123,\n\t\tscore:        123,\n\t\tname:         \"one two three\",\n\t\tupdatedAt:    time.Unix(123, 0).UTC(),\n\t}, {\n\t\tuuid:         uuid.UUID([16]byte{15: 2}),\n\t\tdata:         map[string]any{\"lorem ipsum\": \"dolor sit amet\"},\n\t\tcolor:        ColorGreen,\n\t\tdirection:    DirectionSouth,\n\t\tweekday:      Tuesday,\n\t\ttextArray:    []string{\"four\", \"five\", \"six\"},\n\t\tintArray:     []int{4, 5, 6},\n\t\tint64Array:   []int64{4, 5, 6},\n\t\tint32Array:   []int32{4, 5, 6},\n\t\tfloat64Array: []float64{4, 5, 6},\n\t\tfloat32Array: []float32{4, 5, 6},\n\t\tboolArray:    []bool{false, true, false},\n\t\tbytes:        []byte{4, 5, 6},\n\t\tisActive:     true,\n\t\tprice:        456,\n\t\tscore:        456,\n\t\tname:         \"four five six\",\n\t\tupdatedAt:    time.Unix(456, 0).UTC(),\n\t}, {\n\t\tuuid:         uuid.UUID([16]byte{15: 3}),\n\t\tdata:         map[string]any{\"lorem ipsum\": \"dolor sit amet\"},\n\t\tcolor:        ColorBlue,\n\t\tdirection:    DirectionEast,\n\t\tweekday:      Wednesday,\n\t\ttextArray:    []string{\"seven\", \"eight\", \"nine\"},\n\t\tintArray:     []int{7, 8, 9},\n\t\tfloat64Array: []float64{7, 8, 9},\n\t\tboolArray:    []bool{false, false, true},\n\t\tbytes:        []byte{7, 8, 9},\n\t\tisActive:     true,\n\t\tprice:        789,\n\t\tscore:        789,\n\t\tname:         \"seven eight nine\",\n\t\tupdatedAt:    time.Unix(789, 0).UTC(),\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.dialect, func(t *testing.T) {\n\t\t\tif tt.dsn == \"\" {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.Parallel()\n\t\t\tdsn := preprocessDSN(tt.dialect, tt.dsn)\n\t\t\tdb, err := sql.Open(tt.driver, dsn)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\t_, err = db.Exec(tt.teardown)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\t_, err = db.Exec(tt.setup)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\tdb.Exec(tt.teardown)\n\t\t\t}()\n\n\t\t\t// Insert the data.\n\t\t\tresult, err := Exec(Log(db), InsertInto(TABLE00).\n\t\t\t\tColumnValues(func(col *Column) {\n\t\t\t\t\tfor _, value := range table00Values {\n\t\t\t\t\t\tcol.SetUUID(TABLE00.UUID, value.uuid)\n\t\t\t\t\t\tcol.SetJSON(TABLE00.DATA, value.data)\n\t\t\t\t\t\tcol.SetEnum(TABLE00.COLOR, value.color)\n\t\t\t\t\t\tcol.SetEnum(TABLE00.DIRECTION, value.direction)\n\t\t\t\t\t\tcol.SetEnum(TABLE00.WEEKDAY, value.weekday)\n\t\t\t\t\t\tcol.SetArray(TABLE00.TEXT_ARRAY, value.textArray)\n\t\t\t\t\t\tcol.SetArray(TABLE00.INT_ARRAY, value.intArray)\n\t\t\t\t\t\tcol.SetArray(TABLE00.INT64_ARRAY, value.int64Array)\n\t\t\t\t\t\tcol.SetArray(TABLE00.INT32_ARRAY, value.int32Array)\n\t\t\t\t\t\tcol.SetArray(TABLE00.FLOAT64_ARRAY, value.float64Array)\n\t\t\t\t\t\tcol.SetArray(TABLE00.FLOAT32_ARRAY, value.float32Array)\n\t\t\t\t\t\tcol.SetArray(TABLE00.BOOL_ARRAY, value.boolArray)\n\t\t\t\t\t\tcol.SetBytes(TABLE00.BYTES, value.bytes)\n\t\t\t\t\t\tcol.SetBool(TABLE00.IS_ACTIVE, value.isActive)\n\t\t\t\t\t\tcol.SetFloat64(TABLE00.PRICE, value.price)\n\t\t\t\t\t\tcol.SetInt64(TABLE00.SCORE, value.score)\n\t\t\t\t\t\tcol.SetString(TABLE00.NAME, value.name)\n\t\t\t\t\t\tcol.SetTime(TABLE00.UPDATED_AT, value.updatedAt)\n\t\t\t\t\t}\n\t\t\t\t}).\n\t\t\t\tSetDialect(tt.dialect),\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(result.RowsAffected, int64(len(table00Values))); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\n\t\t\t// Fetch the data.\n\t\t\tvalues, err := FetchAll(VerboseLog(db), From(TABLE00).\n\t\t\t\tOrderBy(TABLE00.UUID).\n\t\t\t\tSetDialect(tt.dialect),\n\t\t\t\tfunc(row *Row) Table00 {\n\t\t\t\t\tvar value Table00\n\t\t\t\t\trow.UUIDField(&value.uuid, TABLE00.UUID)\n\t\t\t\t\trow.JSONField(&value.data, TABLE00.DATA)\n\t\t\t\t\trow.EnumField(&value.color, TABLE00.COLOR)\n\t\t\t\t\trow.EnumField(&value.direction, TABLE00.DIRECTION)\n\t\t\t\t\trow.EnumField(&value.weekday, TABLE00.WEEKDAY)\n\t\t\t\t\trow.ArrayField(&value.textArray, TABLE00.TEXT_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.intArray, TABLE00.INT_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.int64Array, TABLE00.INT64_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.int32Array, TABLE00.INT32_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.float64Array, TABLE00.FLOAT64_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.float32Array, TABLE00.FLOAT32_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.boolArray, TABLE00.BOOL_ARRAY)\n\t\t\t\t\tvalue.bytes = row.BytesField(TABLE00.BYTES)\n\t\t\t\t\tvalue.isActive = row.BoolField(TABLE00.IS_ACTIVE)\n\t\t\t\t\tvalue.price = row.Float64Field(TABLE00.PRICE)\n\t\t\t\t\tvalue.score = row.Int64Field(TABLE00.SCORE)\n\t\t\t\t\tvalue.name = row.StringField(TABLE00.NAME)\n\t\t\t\t\tvalue.updatedAt = row.TimeField(TABLE00.UPDATED_AT)\n\t\t\t\t\t// make sure Columns, ColumnTypes and Values are all\n\t\t\t\t\t// callable inside the rowmapper even for dynamic queries.\n\t\t\t\t\tfmt.Println(row.Columns())\n\t\t\t\t\tfmt.Println(row.ColumnTypes())\n\t\t\t\t\tfmt.Println(row.Values())\n\t\t\t\t\treturn value\n\t\t\t\t},\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(values, table00Values); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\n\t\t\texists, err := FetchExists(Log(db), SelectOne().\n\t\t\t\tFrom(TABLE00).\n\t\t\t\tWhere(TABLE00.UUID.EqUUID(table00Values[0].uuid)).\n\t\t\t\tSetDialect(tt.dialect),\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\tif !exists {\n\t\t\t\tt.Errorf(testutil.Callers()+\" expected row with uuid = %q to exist, got false\", table00Values[0].uuid.String())\n\t\t\t}\n\n\t\t\t// SQLServer driver *still* doesn't support NULL UUIDs 🙄, skip\n\t\t\t// NULL testing for SQL Server.\n\t\t\t// https://github.com/denisenkom/go-mssqldb/issues/196\n\t\t\tif tt.dialect == \"sqlserver\" {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Insert NULLs.\n\t\t\t_, err = Exec(Log(db), InsertInto(TABLE00).\n\t\t\t\tColumnValues(func(col *Column) {\n\t\t\t\t\tcol.Set(TABLE00.UUID, nil)\n\t\t\t\t\tcol.Set(TABLE00.DATA, nil)\n\t\t\t\t\tcol.Set(TABLE00.COLOR, nil)\n\t\t\t\t\tcol.Set(TABLE00.DIRECTION, nil)\n\t\t\t\t\tcol.Set(TABLE00.WEEKDAY, nil)\n\t\t\t\t\tcol.Set(TABLE00.TEXT_ARRAY, nil)\n\t\t\t\t\tcol.Set(TABLE00.INT_ARRAY, nil)\n\t\t\t\t\tcol.Set(TABLE00.INT64_ARRAY, nil)\n\t\t\t\t\tcol.Set(TABLE00.INT32_ARRAY, nil)\n\t\t\t\t\tcol.Set(TABLE00.FLOAT64_ARRAY, nil)\n\t\t\t\t\tcol.Set(TABLE00.FLOAT32_ARRAY, nil)\n\t\t\t\t\tcol.Set(TABLE00.BOOL_ARRAY, nil)\n\t\t\t\t\tcol.Set(TABLE00.BYTES, nil)\n\t\t\t\t\tcol.Set(TABLE00.IS_ACTIVE, nil)\n\t\t\t\t\tcol.Set(TABLE00.PRICE, nil)\n\t\t\t\t\tcol.Set(TABLE00.SCORE, nil)\n\t\t\t\t\tcol.Set(TABLE00.NAME, nil)\n\t\t\t\t\tcol.Set(TABLE00.UPDATED_AT, nil)\n\t\t\t\t}).\n\t\t\t\tSetDialect(tt.dialect),\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\n\t\t\t// Fetch NULLs.\n\t\t\t_, err = FetchAll(VerboseLog(db), From(TABLE00).\n\t\t\t\tWhere(TABLE00.UUID.IsNull()).\n\t\t\t\tOrderBy(TABLE00.UUID).\n\t\t\t\tSetDialect(tt.dialect),\n\t\t\t\tfunc(row *Row) Table00 {\n\t\t\t\t\tvar value Table00\n\t\t\t\t\trow.UUIDField(&value.uuid, TABLE00.UUID)\n\t\t\t\t\trow.JSONField(&value.data, TABLE00.DATA)\n\t\t\t\t\trow.EnumField(&value.color, TABLE00.COLOR)\n\t\t\t\t\trow.EnumField(&value.direction, TABLE00.DIRECTION)\n\t\t\t\t\trow.EnumField(&value.weekday, TABLE00.WEEKDAY)\n\t\t\t\t\trow.ArrayField(&value.textArray, TABLE00.TEXT_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.intArray, TABLE00.INT_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.int64Array, TABLE00.INT64_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.int32Array, TABLE00.INT32_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.float64Array, TABLE00.FLOAT64_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.float32Array, TABLE00.FLOAT32_ARRAY)\n\t\t\t\t\trow.ArrayField(&value.boolArray, TABLE00.BOOL_ARRAY)\n\t\t\t\t\tvalue.bytes = row.BytesField(TABLE00.BYTES)\n\t\t\t\t\tvalue.isActive = row.BoolField(TABLE00.IS_ACTIVE)\n\t\t\t\t\tvalue.price = row.Float64Field(TABLE00.PRICE)\n\t\t\t\t\tvalue.score = row.Int64Field(TABLE00.SCORE)\n\t\t\t\t\tvalue.name = row.StringField(TABLE00.NAME)\n\t\t\t\t\tvalue.updatedAt = row.TimeField(TABLE00.UPDATED_AT)\n\t\t\t\t\treturn value\n\t\t\t\t},\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRowScan(t *testing.T) {\n\ttable01Values := [][]any{\n\t\t{nil, nil, nil, nil, nil, nil},\n\t\t{123, int64(123), float64(123), \"abc\", true, time.Unix(123, 0).UTC()},\n\t\t{456, int64(456), float64(456), \"def\", true, time.Unix(456, 0).UTC()},\n\t\t{789, int64(789), float64(789), \"ghi\", true, time.Unix(789, 0).UTC()},\n\t}\n\n\ttype TestTable struct {\n\t\tdialect  string\n\t\tdriver   string\n\t\tdsn      string\n\t\tteardown string\n\t\tsetup    string\n\t}\n\n\ttests := []TestTable{{\n\t\tdialect:  DialectSQLite,\n\t\tdriver:   \"sqlite3\",\n\t\tdsn:      \"file:/TestRowScan/sqlite?vfs=memdb&_foreign_keys=true\",\n\t\tteardown: \"DROP TABLE IF EXISTS table01;\",\n\t\tsetup: \"CREATE TABLE table01 (\" +\n\t\t\t\"\\n    id INT\" +\n\t\t\t\"\\n    ,score BIGINT\" +\n\t\t\t\"\\n    ,price REAL\" +\n\t\t\t\"\\n    ,name TEXT\" +\n\t\t\t\"\\n    ,is_active BOOLEAN\" +\n\t\t\t\"\\n    ,updated_at DATETIME\" +\n\t\t\t\"\\n);\",\n\t}, {\n\t\tdialect:  DialectPostgres,\n\t\tdriver:   \"postgres\",\n\t\tdsn:      *postgresDSN,\n\t\tteardown: \"DROP TABLE IF EXISTS table01;\",\n\t\tsetup: \"CREATE TABLE table01 (\" +\n\t\t\t\"\\n    id INT\" +\n\t\t\t\"\\n    ,score BIGINT\" +\n\t\t\t\"\\n    ,price DOUBLE PRECISION\" +\n\t\t\t\"\\n    ,name TEXT\" +\n\t\t\t\"\\n    ,is_active BOOLEAN\" +\n\t\t\t\"\\n    ,updated_at TIMESTAMPTZ\" +\n\t\t\t\"\\n);\",\n\t}, {\n\t\tdialect:  DialectMySQL,\n\t\tdriver:   \"mysql\",\n\t\tdsn:      *mysqlDSN,\n\t\tteardown: \"DROP TABLE IF EXISTS table01;\",\n\t\tsetup: \"CREATE TABLE table01 (\" +\n\t\t\t\"\\n    id INT\" +\n\t\t\t\"\\n    ,score BIGINT\" +\n\t\t\t\"\\n    ,price DOUBLE PRECISION\" +\n\t\t\t\"\\n    ,name VARCHAR(255)\" +\n\t\t\t\"\\n    ,is_active BOOLEAN\" +\n\t\t\t\"\\n    ,updated_at DATETIME\" +\n\t\t\t\"\\n);\",\n\t}, {\n\t\tdialect:  DialectSQLServer,\n\t\tdriver:   \"sqlserver\",\n\t\tdsn:      *sqlserverDSN,\n\t\tteardown: \"DROP TABLE IF EXISTS table01;\",\n\t\tsetup: \"CREATE TABLE table01 (\" +\n\t\t\t\"\\n    id INT\" +\n\t\t\t\"\\n    ,score BIGINT\" +\n\t\t\t\"\\n    ,price DOUBLE PRECISION\" +\n\t\t\t\"\\n    ,name NVARCHAR(255)\" +\n\t\t\t\"\\n    ,is_active BIT\" +\n\t\t\t\"\\n    ,updated_at DATETIME2\" +\n\t\t\t\"\\n);\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.dialect, func(t *testing.T) {\n\t\t\tif tt.dsn == \"\" {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.Parallel()\n\t\t\tdsn := preprocessDSN(tt.dialect, tt.dsn)\n\t\t\tdb, err := sql.Open(tt.driver, dsn)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\t_, err = db.Exec(tt.teardown)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\t_, err = db.Exec(tt.setup)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\tdb.Exec(tt.teardown)\n\t\t\t}()\n\n\t\t\t// Insert values.\n\t\t\tresult, err := Exec(Log(db), InsertQuery{\n\t\t\t\tDialect:     tt.dialect,\n\t\t\t\tInsertTable: Expr(\"table01\"),\n\t\t\t\tColumnMapper: func(col *Column) {\n\t\t\t\t\tfor _, value := range table01Values {\n\t\t\t\t\t\tcol.Set(Expr(\"id\"), value[0])\n\t\t\t\t\t\tcol.Set(Expr(\"score\"), value[1])\n\t\t\t\t\t\tcol.Set(Expr(\"price\"), value[2])\n\t\t\t\t\t\tcol.Set(Expr(\"name\"), value[3])\n\t\t\t\t\t\tcol.Set(Expr(\"is_active\"), value[4])\n\t\t\t\t\t\tcol.Set(Expr(\"updated_at\"), value[5])\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(result.RowsAffected, int64(len(table01Values))); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\n\t\t\tt.Run(\"dynamic SQL query\", func(t *testing.T) {\n\t\t\t\tgotValues, err := FetchAll(db,\n\t\t\t\t\tQueryf(\"SELECT {*} FROM table01 WHERE id IS NOT NULL ORDER BY id\").SetDialect(tt.dialect),\n\t\t\t\t\tfunc(row *Row) []any {\n\t\t\t\t\t\tvar id int\n\t\t\t\t\t\tvar score1 int64\n\t\t\t\t\t\tvar score2 int32\n\t\t\t\t\t\tvar price float64\n\t\t\t\t\t\tvar name string\n\t\t\t\t\t\tvar isActive bool\n\t\t\t\t\t\tvar updatedAt time.Time\n\t\t\t\t\t\trow.Scan(&id, \"id\")\n\t\t\t\t\t\trow.Scan(&score1, \"score\")\n\t\t\t\t\t\trow.Scan(&score2, \"score\")\n\t\t\t\t\t\tif diff := testutil.Diff(score1, int64(score2)); diff != \"\" {\n\t\t\t\t\t\t\tpanic(fmt.Errorf(testutil.Callers() + diff))\n\t\t\t\t\t\t}\n\t\t\t\t\t\trow.Scan(&price, \"price\")\n\t\t\t\t\t\trow.Scan(&name, \"name\")\n\t\t\t\t\t\trow.Scan(&isActive, \"is_active\")\n\t\t\t\t\t\trow.Scan(&updatedAt, \"updated_at\")\n\t\t\t\t\t\treturn []any{id, score1, price, name, isActive, updatedAt}\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t\t}\n\t\t\t\twantValues := [][]any{\n\t\t\t\t\t{123, int64(123), float64(123), \"abc\", true, time.Unix(123, 0).UTC()},\n\t\t\t\t\t{456, int64(456), float64(456), \"def\", true, time.Unix(456, 0).UTC()},\n\t\t\t\t\t{789, int64(789), float64(789), \"ghi\", true, time.Unix(789, 0).UTC()},\n\t\t\t\t}\n\t\t\t\tif diff := testutil.Diff(gotValues, wantValues); diff != \"\" {\n\t\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"dynamic SQL query (null values)\", func(t *testing.T) {\n\t\t\t\tgotValue, err := FetchOne(db,\n\t\t\t\t\tQueryf(\"SELECT {*} FROM table01 WHERE id IS NULL\").SetDialect(tt.dialect),\n\t\t\t\t\tfunc(row *Row) []any {\n\t\t\t\t\t\tvar id int\n\t\t\t\t\t\tvar score1 int64\n\t\t\t\t\t\tvar score2 int32\n\t\t\t\t\t\tvar price float64\n\t\t\t\t\t\tvar name string\n\t\t\t\t\t\tvar isActive bool\n\t\t\t\t\t\tvar updatedAt time.Time\n\t\t\t\t\t\trow.Scan(&id, \"id\")\n\t\t\t\t\t\trow.Scan(&score1, \"score\")\n\t\t\t\t\t\trow.Scan(&score2, \"score\")\n\t\t\t\t\t\tif diff := testutil.Diff(score1, int64(score2)); diff != \"\" {\n\t\t\t\t\t\t\tpanic(fmt.Errorf(testutil.Callers() + diff))\n\t\t\t\t\t\t}\n\t\t\t\t\t\trow.Scan(&price, \"price\")\n\t\t\t\t\t\trow.Scan(&name, \"name\")\n\t\t\t\t\t\trow.Scan(&isActive, \"is_active\")\n\t\t\t\t\t\trow.Scan(&updatedAt, \"updated_at\")\n\t\t\t\t\t\treturn []any{id, score1, price, name, isActive, updatedAt}\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t\t}\n\t\t\t\twantValue := []any{int(0), int64(0), float64(0), \"\", false, time.Time{}}\n\t\t\t\tif diff := testutil.Diff(gotValue, wantValue); diff != \"\" {\n\t\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"dynamic SQL query (null values) (using sql.Null structs)\", func(t *testing.T) {\n\t\t\t\tgotValue, err := FetchOne(db,\n\t\t\t\t\tQueryf(\"SELECT {*} FROM table01 WHERE id IS NULL\").SetDialect(tt.dialect),\n\t\t\t\t\tfunc(row *Row) []any {\n\t\t\t\t\t\tvar id sql.NullInt64\n\t\t\t\t\t\tvar score1 sql.NullInt64\n\t\t\t\t\t\tvar score2 sql.NullInt32\n\t\t\t\t\t\tvar price sql.NullFloat64\n\t\t\t\t\t\tvar name sql.NullString\n\t\t\t\t\t\tvar isActive sql.NullBool\n\t\t\t\t\t\tvar updatedAt sql.NullTime\n\t\t\t\t\t\trow.Scan(&id, \"id\")\n\t\t\t\t\t\trow.Scan(&score1, \"score\")\n\t\t\t\t\t\trow.Scan(&score2, \"score\")\n\t\t\t\t\t\tif diff := testutil.Diff(score1.Int64, int64(score2.Int32)); diff != \"\" {\n\t\t\t\t\t\t\tpanic(fmt.Errorf(testutil.Callers() + diff))\n\t\t\t\t\t\t}\n\t\t\t\t\t\trow.Scan(&price, \"price\")\n\t\t\t\t\t\trow.Scan(&name, \"name\")\n\t\t\t\t\t\trow.Scan(&isActive, \"is_active\")\n\t\t\t\t\t\trow.Scan(&updatedAt, \"updated_at\")\n\t\t\t\t\t\treturn []any{int(id.Int64), score1.Int64, price.Float64, name.String, isActive.Bool, updatedAt.Time}\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t\t}\n\t\t\t\twantValue := []any{int(0), int64(0), float64(0), \"\", false, time.Time{}}\n\t\t\t\tif diff := testutil.Diff(gotValue, wantValue); diff != \"\" {\n\t\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"static SQL query\", func(t *testing.T) {\n\t\t\t\t// Raw SQL query with.\n\t\t\t\tgotValues, err := FetchAll(Log(db),\n\t\t\t\t\tQueryf(\"SELECT id, score, price, name, is_active, updated_at FROM table01 WHERE id IS NOT NULL ORDER BY id\").SetDialect(tt.dialect),\n\t\t\t\t\tfunc(row *Row) []any {\n\t\t\t\t\t\treturn []any{\n\t\t\t\t\t\t\trow.Int(\"id\"),\n\t\t\t\t\t\t\trow.Int64(\"score\"),\n\t\t\t\t\t\t\trow.Float64(\"price\"),\n\t\t\t\t\t\t\trow.String(\"name\"),\n\t\t\t\t\t\t\trow.Bool(\"is_active\"),\n\t\t\t\t\t\t\trow.Time(\"updated_at\"),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t\t}\n\t\t\t\twantValues := [][]any{\n\t\t\t\t\t{123, int64(123), float64(123), \"abc\", true, time.Unix(123, 0).UTC()},\n\t\t\t\t\t{456, int64(456), float64(456), \"def\", true, time.Unix(456, 0).UTC()},\n\t\t\t\t\t{789, int64(789), float64(789), \"ghi\", true, time.Unix(789, 0).UTC()},\n\t\t\t\t}\n\t\t\t\tif diff := testutil.Diff(gotValues, wantValues); diff != \"\" {\n\t\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"static SQL query (raw Values)\", func(t *testing.T) {\n\t\t\t\tgotValues, err := FetchAll(db,\n\t\t\t\t\tQueryf(\"SELECT id, score, price, name, is_active, updated_at FROM table01 WHERE id IS NOT NULL ORDER BY id\").SetDialect(tt.dialect),\n\t\t\t\t\tfunc(row *Row) []any {\n\t\t\t\t\t\tcolumns := row.Columns()\n\t\t\t\t\t\tcolumnTypes := row.ColumnTypes()\n\t\t\t\t\t\tvalues := row.Values()\n\t\t\t\t\t\tif len(columns) != len(columnTypes) || len(columnTypes) != len(values) {\n\t\t\t\t\t\t\tpanic(fmt.Errorf(testutil.Callers()+\" length of columns/columnTypes/values don't match: %v %v %v\", columns, columnTypes, values))\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn values\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t\t}\n\t\t\t\t// We need to tweak wantValues depending on the dialect because\n\t\t\t\t// we are at the mercy of whatever that dialect's database\n\t\t\t\t// driver decides to return.\n\t\t\t\tvar wantValues [][]any\n\t\t\t\tswitch tt.dialect {\n\t\t\t\tcase DialectSQLite, DialectPostgres, DialectSQLServer:\n\t\t\t\t\twantValues = [][]any{\n\t\t\t\t\t\t{int64(123), int64(123), float64(123), \"abc\", true, time.Unix(123, 0).UTC()},\n\t\t\t\t\t\t{int64(456), int64(456), float64(456), \"def\", true, time.Unix(456, 0).UTC()},\n\t\t\t\t\t\t{int64(789), int64(789), float64(789), \"ghi\", true, time.Unix(789, 0).UTC()},\n\t\t\t\t\t}\n\t\t\t\tcase DialectMySQL:\n\t\t\t\t\twantValues = [][]any{\n\t\t\t\t\t\t{[]byte(\"123\"), []byte(\"123\"), []byte(\"123\"), []byte(\"abc\"), []byte(\"1\"), time.Unix(123, 0).UTC()},\n\t\t\t\t\t\t{[]byte(\"456\"), []byte(\"456\"), []byte(\"456\"), []byte(\"def\"), []byte(\"1\"), time.Unix(456, 0).UTC()},\n\t\t\t\t\t\t{[]byte(\"789\"), []byte(\"789\"), []byte(\"789\"), []byte(\"ghi\"), []byte(\"1\"), time.Unix(789, 0).UTC()},\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif diff := testutil.Diff(gotValues, wantValues); diff != \"\" {\n\t\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"static SQL query (null values)\", func(t *testing.T) {\n\t\t\t\tgotValue, err := FetchOne(db,\n\t\t\t\t\tQueryf(\"SELECT id, score, price, name, is_active, updated_at FROM table01 WHERE id IS NULL\").SetDialect(tt.dialect),\n\t\t\t\t\tfunc(row *Row) []any {\n\t\t\t\t\t\tcolumns := row.Columns()\n\t\t\t\t\t\tcolumnTypes := row.ColumnTypes()\n\t\t\t\t\t\tvalues := row.Values()\n\t\t\t\t\t\tif len(columns) != len(columnTypes) || len(columnTypes) != len(values) {\n\t\t\t\t\t\t\tpanic(fmt.Errorf(testutil.Callers()+\" length of columns/columnTypes/values don't match: %v %v %v\", columns, columnTypes, values))\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn values\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t\t}\n\t\t\t\tif diff := testutil.Diff(gotValue, []any{nil, nil, nil, nil, nil, nil}); diff != \"\" {\n\t\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"static SQL query (null values) (using sql.Null structs)\", func(t *testing.T) {\n\t\t\t\tgotValue, err := FetchOne(db,\n\t\t\t\t\tQueryf(\"SELECT id, score, price, name, is_active, updated_at FROM table01 WHERE id IS NULL\").SetDialect(tt.dialect),\n\t\t\t\t\tfunc(row *Row) []any {\n\t\t\t\t\t\treturn []any{\n\t\t\t\t\t\t\trow.NullInt64(\"score\"),\n\t\t\t\t\t\t\trow.NullFloat64(\"price\"),\n\t\t\t\t\t\t\trow.NullString(\"name\"),\n\t\t\t\t\t\t\trow.NullBool(\"is_active\"),\n\t\t\t\t\t\t\trow.NullTime(\"updated_at\"),\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t\t}\n\t\t\t\twantValues := []any{sql.NullInt64{}, sql.NullFloat64{}, sql.NullString{}, sql.NullBool{}, sql.NullTime{}}\n\t\t\t\tif diff := testutil.Diff(gotValue, wantValues); diff != \"\" {\n\t\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc preprocessDSN(dialect string, dsn string) string {\n\tswitch dialect {\n\tcase DialectPostgres:\n\t\tbefore, after, _ := strings.Cut(dsn, \"?\")\n\t\tq, err := url.ParseQuery(after)\n\t\tif err != nil {\n\t\t\treturn dsn\n\t\t}\n\t\tif !q.Has(\"sslmode\") {\n\t\t\tq.Set(\"sslmode\", \"disable\")\n\t\t}\n\t\tif !q.Has(\"binary_parameters\") {\n\t\t\tq.Set(\"binary_parameters\", \"yes\")\n\t\t}\n\t\treturn before + \"?\" + q.Encode()\n\tcase DialectMySQL:\n\t\tbefore, after, _ := strings.Cut(strings.TrimPrefix(dsn, \"mysql://\"), \"?\")\n\t\tq, err := url.ParseQuery(after)\n\t\tif err != nil {\n\t\t\treturn dsn\n\t\t}\n\t\tif !q.Has(\"allowAllFiles\") {\n\t\t\tq.Set(\"allowAllFiles\", \"true\")\n\t\t}\n\t\tif !q.Has(\"multiStatements\") {\n\t\t\tq.Set(\"multiStatements\", \"true\")\n\t\t}\n\t\tif !q.Has(\"parseTime\") {\n\t\t\tq.Set(\"parseTime\", \"true\")\n\t\t}\n\t\treturn before + \"?\" + q.Encode()\n\tdefault:\n\t\treturn dsn\n\t}\n}\n"
  },
  {
    "path": "internal/googleuuid/googleuuid.go",
    "content": "// Copyright (c) 2009,2014 Google Inc. All rights reserved.\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//    * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//    * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//    * Neither the name of Google Inc. nor the names of its\n// contributors may be used to endorse or promote products derived from\n// this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\npackage googleuuid\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\n// ParseBytes decodes b into a UUID or returns an error.  Both the UUID form of\n// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and\n// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.\nfunc ParseBytes(b []byte) (uuid [16]byte, err error) {\n\tswitch len(b) {\n\tcase 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n\tcase 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n\t\tif !bytes.Equal(bytes.ToLower(b[:9]), []byte(\"urn:uuid:\")) {\n\t\t\treturn uuid, fmt.Errorf(\"invalid urn prefix: %q\", b[:9])\n\t\t}\n\t\tb = b[9:]\n\tcase 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\n\t\tb = b[1:]\n\tcase 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\t\tvar ok bool\n\t\tfor i := 0; i < 32; i += 2 {\n\t\t\tuuid[i/2], ok = xtob(b[i], b[i+1])\n\t\t\tif !ok {\n\t\t\t\treturn uuid, errors.New(\"invalid UUID format\")\n\t\t\t}\n\t\t}\n\t\treturn uuid, nil\n\tdefault:\n\t\treturn uuid, fmt.Errorf(\"invalid UUID length: %d\", len(b))\n\t}\n\t// s is now at least 36 bytes long\n\t// it must be of the form  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n\tif b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {\n\t\treturn uuid, errors.New(\"invalid UUID format\")\n\t}\n\tfor i, x := range [16]int{\n\t\t0, 2, 4, 6,\n\t\t9, 11,\n\t\t14, 16,\n\t\t19, 21,\n\t\t24, 26, 28, 30, 32, 34,\n\t} {\n\t\tv, ok := xtob(b[x], b[x+1])\n\t\tif !ok {\n\t\t\treturn uuid, errors.New(\"invalid UUID format\")\n\t\t}\n\t\tuuid[i] = v\n\t}\n\treturn uuid, nil\n}\n\nfunc Parse(s string) (uuid [16]byte, err error) {\n\tif len(s) != 36 {\n\t\tif len(s) != 36+9 {\n\t\t\treturn uuid, fmt.Errorf(\"invalid UUID length: %d\", len(s))\n\t\t}\n\t\tif strings.ToLower(s[:9]) != \"urn:uuid:\" {\n\t\t\treturn uuid, fmt.Errorf(\"invalid urn prefix: %q\", s[:9])\n\t\t}\n\t\ts = s[9:]\n\t}\n\tif s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {\n\t\treturn uuid, errors.New(\"invalid UUID format\")\n\t}\n\tfor i, x := range [16]int{\n\t\t0, 2, 4, 6,\n\t\t9, 11,\n\t\t14, 16,\n\t\t19, 21,\n\t\t24, 26, 28, 30, 32, 34,\n\t} {\n\t\tv, ok := xtob(s[x], s[x+1])\n\t\tif !ok {\n\t\t\treturn uuid, errors.New(\"invalid UUID format\")\n\t\t}\n\t\tuuid[i] = v\n\t}\n\treturn uuid, nil\n}\n\n// xvalues returns the value of a byte as a hexadecimal digit or 255.\nvar xvalues = [256]byte{\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,\n\t255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n\t255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n}\n\n// xtob converts hex characters x1 and x2 into a byte.\nfunc xtob(x1, x2 byte) (byte, bool) {\n\tb1 := xvalues[x1]\n\tb2 := xvalues[x2]\n\treturn (b1 << 4) | b2, b1 != 255 && b2 != 255\n}\n\n// var buf [36]byte; encodeHex(buf[:], [16]byte{...}); string(buf[:])\nfunc EncodeHex(dst []byte, uuid [16]byte) {\n\thex.Encode(dst[:], uuid[:4])\n\tdst[8] = '-'\n\thex.Encode(dst[9:13], uuid[4:6])\n\tdst[13] = '-'\n\thex.Encode(dst[14:18], uuid[6:8])\n\tdst[18] = '-'\n\thex.Encode(dst[19:23], uuid[8:10])\n\tdst[23] = '-'\n\thex.Encode(dst[24:], uuid[10:])\n}\n"
  },
  {
    "path": "internal/pqarray/pqarray.go",
    "content": "// Copyright (c) 2011-2013, 'pq' Contributors Portions Copyright (C) 2011 Blake Mizerany\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage pqarray\n\nimport (\n\t\"bytes\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar typeByteSlice = reflect.TypeOf([]byte{})\nvar typeDriverValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem()\nvar typeSQLScanner = reflect.TypeOf((*sql.Scanner)(nil)).Elem()\n\n// Array returns the optimal driver.Valuer and sql.Scanner for an array or\n// slice of any dimension.\n//\n// For example:\n//\n//\tdb.Query(`SELECT * FROM t WHERE id = ANY($1)`, pq.Array([]int{235, 401}))\n//\n//\tvar x []sql.NullInt64\n//\tdb.QueryRow(`SELECT ARRAY[235, 401]`).Scan(pq.Array(&x))\n//\n// Scanning multi-dimensional arrays is not supported.  Arrays where the lower\n// bound is not one (such as `[0:0]={1}') are not supported.\nfunc Array(a interface{}) interface {\n\tdriver.Valuer\n\tsql.Scanner\n} {\n\tswitch a := a.(type) {\n\tcase []bool:\n\t\treturn (*BoolArray)(&a)\n\tcase []float64:\n\t\treturn (*Float64Array)(&a)\n\tcase []float32:\n\t\treturn (*Float32Array)(&a)\n\tcase []int64:\n\t\treturn (*Int64Array)(&a)\n\tcase []int32:\n\t\treturn (*Int32Array)(&a)\n\tcase []string:\n\t\treturn (*StringArray)(&a)\n\tcase [][]byte:\n\t\treturn (*ByteaArray)(&a)\n\n\tcase *[]bool:\n\t\treturn (*BoolArray)(a)\n\tcase *[]float64:\n\t\treturn (*Float64Array)(a)\n\tcase *[]float32:\n\t\treturn (*Float32Array)(a)\n\tcase *[]int64:\n\t\treturn (*Int64Array)(a)\n\tcase *[]int32:\n\t\treturn (*Int32Array)(a)\n\tcase *[]string:\n\t\treturn (*StringArray)(a)\n\tcase *[][]byte:\n\t\treturn (*ByteaArray)(a)\n\t}\n\n\treturn GenericArray{a}\n}\n\n// ArrayDelimiter may be optionally implemented by driver.Valuer or sql.Scanner\n// to override the array delimiter used by GenericArray.\ntype ArrayDelimiter interface {\n\t// ArrayDelimiter returns the delimiter character(s) for this element's type.\n\tArrayDelimiter() string\n}\n\n// BoolArray represents a one-dimensional array of the PostgreSQL boolean type.\ntype BoolArray []bool\n\n// Scan implements the sql.Scanner interface.\nfunc (a *BoolArray) Scan(src interface{}) error {\n\tswitch src := src.(type) {\n\tcase []byte:\n\t\treturn a.scanBytes(src)\n\tcase string:\n\t\treturn a.scanBytes([]byte(src))\n\tcase nil:\n\t\t*a = nil\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"pq: cannot convert %T to BoolArray\", src)\n}\n\nfunc (a *BoolArray) scanBytes(src []byte) error {\n\telems, err := scanLinearArray(src, []byte{','}, \"BoolArray\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif *a != nil && len(elems) == 0 {\n\t\t*a = (*a)[:0]\n\t} else {\n\t\tb := make(BoolArray, len(elems))\n\t\tfor i, v := range elems {\n\t\t\tif len(v) != 1 {\n\t\t\t\treturn fmt.Errorf(\"pq: could not parse boolean array index %d: invalid boolean %q\", i, v)\n\t\t\t}\n\t\t\tswitch v[0] {\n\t\t\tcase 't':\n\t\t\t\tb[i] = true\n\t\t\tcase 'f':\n\t\t\t\tb[i] = false\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"pq: could not parse boolean array index %d: invalid boolean %q\", i, v)\n\t\t\t}\n\t\t}\n\t\t*a = b\n\t}\n\treturn nil\n}\n\n// Value implements the driver.Valuer interface.\nfunc (a BoolArray) Value() (driver.Value, error) {\n\tif a == nil {\n\t\treturn nil, nil\n\t}\n\n\tif n := len(a); n > 0 {\n\t\t// There will be exactly two curly brackets, N bytes of values,\n\t\t// and N-1 bytes of delimiters.\n\t\tb := make([]byte, 1+2*n)\n\n\t\tfor i := 0; i < n; i++ {\n\t\t\tb[2*i] = ','\n\t\t\tif a[i] {\n\t\t\t\tb[1+2*i] = 't'\n\t\t\t} else {\n\t\t\t\tb[1+2*i] = 'f'\n\t\t\t}\n\t\t}\n\n\t\tb[0] = '{'\n\t\tb[2*n] = '}'\n\n\t\treturn string(b), nil\n\t}\n\n\treturn \"{}\", nil\n}\n\n// ByteaArray represents a one-dimensional array of the PostgreSQL bytea type.\ntype ByteaArray [][]byte\n\n// Scan implements the sql.Scanner interface.\nfunc (a *ByteaArray) Scan(src interface{}) error {\n\tswitch src := src.(type) {\n\tcase []byte:\n\t\treturn a.scanBytes(src)\n\tcase string:\n\t\treturn a.scanBytes([]byte(src))\n\tcase nil:\n\t\t*a = nil\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"pq: cannot convert %T to ByteaArray\", src)\n}\n\nfunc (a *ByteaArray) scanBytes(src []byte) error {\n\telems, err := scanLinearArray(src, []byte{','}, \"ByteaArray\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif *a != nil && len(elems) == 0 {\n\t\t*a = (*a)[:0]\n\t} else {\n\t\tb := make(ByteaArray, len(elems))\n\t\tfor i, v := range elems {\n\t\t\tb[i], err = parseBytea(v)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"could not parse bytea array index %d: %s\", i, err.Error())\n\t\t\t}\n\t\t}\n\t\t*a = b\n\t}\n\treturn nil\n}\n\n// Value implements the driver.Valuer interface. It uses the \"hex\" format which\n// is only supported on PostgreSQL 9.0 or newer.\nfunc (a ByteaArray) Value() (driver.Value, error) {\n\tif a == nil {\n\t\treturn nil, nil\n\t}\n\n\tif n := len(a); n > 0 {\n\t\t// There will be at least two curly brackets, 2*N bytes of quotes,\n\t\t// 3*N bytes of hex formatting, and N-1 bytes of delimiters.\n\t\tsize := 1 + 6*n\n\t\tfor _, x := range a {\n\t\t\tsize += hex.EncodedLen(len(x))\n\t\t}\n\n\t\tb := make([]byte, size)\n\n\t\tfor i, s := 0, b; i < n; i++ {\n\t\t\to := copy(s, `,\"\\\\x`)\n\t\t\to += hex.Encode(s[o:], a[i])\n\t\t\ts[o] = '\"'\n\t\t\ts = s[o+1:]\n\t\t}\n\n\t\tb[0] = '{'\n\t\tb[size-1] = '}'\n\n\t\treturn string(b), nil\n\t}\n\n\treturn \"{}\", nil\n}\n\n// Float64Array represents a one-dimensional array of the PostgreSQL double\n// precision type.\ntype Float64Array []float64\n\n// Scan implements the sql.Scanner interface.\nfunc (a *Float64Array) Scan(src interface{}) error {\n\tswitch src := src.(type) {\n\tcase []byte:\n\t\treturn a.scanBytes(src)\n\tcase string:\n\t\treturn a.scanBytes([]byte(src))\n\tcase nil:\n\t\t*a = nil\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"pq: cannot convert %T to Float64Array\", src)\n}\n\nfunc (a *Float64Array) scanBytes(src []byte) error {\n\telems, err := scanLinearArray(src, []byte{','}, \"Float64Array\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif *a != nil && len(elems) == 0 {\n\t\t*a = (*a)[:0]\n\t} else {\n\t\tb := make(Float64Array, len(elems))\n\t\tfor i, v := range elems {\n\t\t\tif b[i], err = strconv.ParseFloat(string(v), 64); err != nil {\n\t\t\t\treturn fmt.Errorf(\"pq: parsing array element index %d: %v\", i, err)\n\t\t\t}\n\t\t}\n\t\t*a = b\n\t}\n\treturn nil\n}\n\n// Value implements the driver.Valuer interface.\nfunc (a Float64Array) Value() (driver.Value, error) {\n\tif a == nil {\n\t\treturn nil, nil\n\t}\n\n\tif n := len(a); n > 0 {\n\t\t// There will be at least two curly brackets, N bytes of values,\n\t\t// and N-1 bytes of delimiters.\n\t\tb := make([]byte, 1, 1+2*n)\n\t\tb[0] = '{'\n\n\t\tb = strconv.AppendFloat(b, a[0], 'f', -1, 64)\n\t\tfor i := 1; i < n; i++ {\n\t\t\tb = append(b, ',')\n\t\t\tb = strconv.AppendFloat(b, a[i], 'f', -1, 64)\n\t\t}\n\n\t\treturn string(append(b, '}')), nil\n\t}\n\n\treturn \"{}\", nil\n}\n\n// Float32Array represents a one-dimensional array of the PostgreSQL double\n// precision type.\ntype Float32Array []float32\n\n// Scan implements the sql.Scanner interface.\nfunc (a *Float32Array) Scan(src interface{}) error {\n\tswitch src := src.(type) {\n\tcase []byte:\n\t\treturn a.scanBytes(src)\n\tcase string:\n\t\treturn a.scanBytes([]byte(src))\n\tcase nil:\n\t\t*a = nil\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"pq: cannot convert %T to Float32Array\", src)\n}\n\nfunc (a *Float32Array) scanBytes(src []byte) error {\n\telems, err := scanLinearArray(src, []byte{','}, \"Float32Array\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif *a != nil && len(elems) == 0 {\n\t\t*a = (*a)[:0]\n\t} else {\n\t\tb := make(Float32Array, len(elems))\n\t\tfor i, v := range elems {\n\t\t\tvar x float64\n\t\t\tif x, err = strconv.ParseFloat(string(v), 32); err != nil {\n\t\t\t\treturn fmt.Errorf(\"pq: parsing array element index %d: %v\", i, err)\n\t\t\t}\n\t\t\tb[i] = float32(x)\n\t\t}\n\t\t*a = b\n\t}\n\treturn nil\n}\n\n// Value implements the driver.Valuer interface.\nfunc (a Float32Array) Value() (driver.Value, error) {\n\tif a == nil {\n\t\treturn nil, nil\n\t}\n\n\tif n := len(a); n > 0 {\n\t\t// There will be at least two curly brackets, N bytes of values,\n\t\t// and N-1 bytes of delimiters.\n\t\tb := make([]byte, 1, 1+2*n)\n\t\tb[0] = '{'\n\n\t\tb = strconv.AppendFloat(b, float64(a[0]), 'f', -1, 32)\n\t\tfor i := 1; i < n; i++ {\n\t\t\tb = append(b, ',')\n\t\t\tb = strconv.AppendFloat(b, float64(a[i]), 'f', -1, 32)\n\t\t}\n\n\t\treturn string(append(b, '}')), nil\n\t}\n\n\treturn \"{}\", nil\n}\n\n// GenericArray implements the driver.Valuer and sql.Scanner interfaces for\n// an array or slice of any dimension.\ntype GenericArray struct{ A interface{} }\n\nfunc (GenericArray) evaluateDestination(rt reflect.Type) (reflect.Type, func([]byte, reflect.Value) error, string) {\n\tvar assign func([]byte, reflect.Value) error\n\tvar del = \",\"\n\n\t// TODO calculate the assign function for other types\n\t// TODO repeat this section on the element type of arrays or slices (multidimensional)\n\t{\n\t\tif reflect.PtrTo(rt).Implements(typeSQLScanner) {\n\t\t\t// dest is always addressable because it is an element of a slice.\n\t\t\tassign = func(src []byte, dest reflect.Value) (err error) {\n\t\t\t\tss := dest.Addr().Interface().(sql.Scanner)\n\t\t\t\tif src == nil {\n\t\t\t\t\terr = ss.Scan(nil)\n\t\t\t\t} else {\n\t\t\t\t\terr = ss.Scan(src)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tgoto FoundType\n\t\t}\n\n\t\tassign = func([]byte, reflect.Value) error {\n\t\t\treturn fmt.Errorf(\"pq: scanning to %s is not implemented; only sql.Scanner\", rt)\n\t\t}\n\t}\n\nFoundType:\n\n\tif ad, ok := reflect.Zero(rt).Interface().(ArrayDelimiter); ok {\n\t\tdel = ad.ArrayDelimiter()\n\t}\n\n\treturn rt, assign, del\n}\n\n// Scan implements the sql.Scanner interface.\nfunc (a GenericArray) Scan(src interface{}) error {\n\tdpv := reflect.ValueOf(a.A)\n\tswitch {\n\tcase dpv.Kind() != reflect.Ptr:\n\t\treturn fmt.Errorf(\"pq: destination %T is not a pointer to array or slice\", a.A)\n\tcase dpv.IsNil():\n\t\treturn fmt.Errorf(\"pq: destination %T is nil\", a.A)\n\t}\n\n\tdv := dpv.Elem()\n\tswitch dv.Kind() {\n\tcase reflect.Slice:\n\tcase reflect.Array:\n\tdefault:\n\t\treturn fmt.Errorf(\"pq: destination %T is not a pointer to array or slice\", a.A)\n\t}\n\n\tswitch src := src.(type) {\n\tcase []byte:\n\t\treturn a.scanBytes(src, dv)\n\tcase string:\n\t\treturn a.scanBytes([]byte(src), dv)\n\tcase nil:\n\t\tif dv.Kind() == reflect.Slice {\n\t\t\tdv.Set(reflect.Zero(dv.Type()))\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"pq: cannot convert %T to %s\", src, dv.Type())\n}\n\nfunc (a GenericArray) scanBytes(src []byte, dv reflect.Value) error {\n\tdtype, assign, del := a.evaluateDestination(dv.Type().Elem())\n\tdims, elems, err := parseArray(src, []byte(del))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// TODO allow multidimensional\n\n\tif len(dims) > 1 {\n\t\treturn fmt.Errorf(\"pq: scanning from multidimensional ARRAY%s is not implemented\",\n\t\t\tstrings.Replace(fmt.Sprint(dims), \" \", \"][\", -1))\n\t}\n\n\t// Treat a zero-dimensional array like an array with a single dimension of zero.\n\tif len(dims) == 0 {\n\t\tdims = append(dims, 0)\n\t}\n\n\tfor i, rt := 0, dv.Type(); i < len(dims); i, rt = i+1, rt.Elem() {\n\t\tswitch rt.Kind() {\n\t\tcase reflect.Slice:\n\t\tcase reflect.Array:\n\t\t\tif rt.Len() != dims[i] {\n\t\t\t\treturn fmt.Errorf(\"pq: cannot convert ARRAY%s to %s\",\n\t\t\t\t\tstrings.Replace(fmt.Sprint(dims), \" \", \"][\", -1), dv.Type())\n\t\t\t}\n\t\tdefault:\n\t\t\t// TODO handle multidimensional\n\t\t}\n\t}\n\n\tvalues := reflect.MakeSlice(reflect.SliceOf(dtype), len(elems), len(elems))\n\tfor i, e := range elems {\n\t\tif err := assign(e, values.Index(i)); err != nil {\n\t\t\treturn fmt.Errorf(\"pq: parsing array element index %d: %v\", i, err)\n\t\t}\n\t}\n\n\t// TODO handle multidimensional\n\n\tswitch dv.Kind() {\n\tcase reflect.Slice:\n\t\tdv.Set(values.Slice(0, dims[0]))\n\tcase reflect.Array:\n\t\tfor i := 0; i < dims[0]; i++ {\n\t\t\tdv.Index(i).Set(values.Index(i))\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Value implements the driver.Valuer interface.\nfunc (a GenericArray) Value() (driver.Value, error) {\n\tif a.A == nil {\n\t\treturn nil, nil\n\t}\n\n\trv := reflect.ValueOf(a.A)\n\n\tswitch rv.Kind() {\n\tcase reflect.Slice:\n\t\tif rv.IsNil() {\n\t\t\treturn nil, nil\n\t\t}\n\tcase reflect.Array:\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"pq: Unable to convert %T to array\", a.A)\n\t}\n\n\tif n := rv.Len(); n > 0 {\n\t\t// There will be at least two curly brackets, N bytes of values,\n\t\t// and N-1 bytes of delimiters.\n\t\tb := make([]byte, 0, 1+2*n)\n\n\t\tb, _, err := appendArray(b, rv, n)\n\t\treturn string(b), err\n\t}\n\n\treturn \"{}\", nil\n}\n\n// Int64Array represents a one-dimensional array of the PostgreSQL integer types.\ntype Int64Array []int64\n\n// Scan implements the sql.Scanner interface.\nfunc (a *Int64Array) Scan(src interface{}) error {\n\tswitch src := src.(type) {\n\tcase []byte:\n\t\treturn a.scanBytes(src)\n\tcase string:\n\t\treturn a.scanBytes([]byte(src))\n\tcase nil:\n\t\t*a = nil\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"pq: cannot convert %T to Int64Array\", src)\n}\n\nfunc (a *Int64Array) scanBytes(src []byte) error {\n\telems, err := scanLinearArray(src, []byte{','}, \"Int64Array\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif *a != nil && len(elems) == 0 {\n\t\t*a = (*a)[:0]\n\t} else {\n\t\tb := make(Int64Array, len(elems))\n\t\tfor i, v := range elems {\n\t\t\tif b[i], err = strconv.ParseInt(string(v), 10, 64); err != nil {\n\t\t\t\treturn fmt.Errorf(\"pq: parsing array element index %d: %v\", i, err)\n\t\t\t}\n\t\t}\n\t\t*a = b\n\t}\n\treturn nil\n}\n\n// Value implements the driver.Valuer interface.\nfunc (a Int64Array) Value() (driver.Value, error) {\n\tif a == nil {\n\t\treturn nil, nil\n\t}\n\n\tif n := len(a); n > 0 {\n\t\t// There will be at least two curly brackets, N bytes of values,\n\t\t// and N-1 bytes of delimiters.\n\t\tb := make([]byte, 1, 1+2*n)\n\t\tb[0] = '{'\n\n\t\tb = strconv.AppendInt(b, a[0], 10)\n\t\tfor i := 1; i < n; i++ {\n\t\t\tb = append(b, ',')\n\t\t\tb = strconv.AppendInt(b, a[i], 10)\n\t\t}\n\n\t\treturn string(append(b, '}')), nil\n\t}\n\n\treturn \"{}\", nil\n}\n\n// Int32Array represents a one-dimensional array of the PostgreSQL integer types.\ntype Int32Array []int32\n\n// Scan implements the sql.Scanner interface.\nfunc (a *Int32Array) Scan(src interface{}) error {\n\tswitch src := src.(type) {\n\tcase []byte:\n\t\treturn a.scanBytes(src)\n\tcase string:\n\t\treturn a.scanBytes([]byte(src))\n\tcase nil:\n\t\t*a = nil\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"pq: cannot convert %T to Int32Array\", src)\n}\n\nfunc (a *Int32Array) scanBytes(src []byte) error {\n\telems, err := scanLinearArray(src, []byte{','}, \"Int32Array\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif *a != nil && len(elems) == 0 {\n\t\t*a = (*a)[:0]\n\t} else {\n\t\tb := make(Int32Array, len(elems))\n\t\tfor i, v := range elems {\n\t\t\tx, err := strconv.ParseInt(string(v), 10, 32)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"pq: parsing array element index %d: %v\", i, err)\n\t\t\t}\n\t\t\tb[i] = int32(x)\n\t\t}\n\t\t*a = b\n\t}\n\treturn nil\n}\n\n// Value implements the driver.Valuer interface.\nfunc (a Int32Array) Value() (driver.Value, error) {\n\tif a == nil {\n\t\treturn nil, nil\n\t}\n\n\tif n := len(a); n > 0 {\n\t\t// There will be at least two curly brackets, N bytes of values,\n\t\t// and N-1 bytes of delimiters.\n\t\tb := make([]byte, 1, 1+2*n)\n\t\tb[0] = '{'\n\n\t\tb = strconv.AppendInt(b, int64(a[0]), 10)\n\t\tfor i := 1; i < n; i++ {\n\t\t\tb = append(b, ',')\n\t\t\tb = strconv.AppendInt(b, int64(a[i]), 10)\n\t\t}\n\n\t\treturn string(append(b, '}')), nil\n\t}\n\n\treturn \"{}\", nil\n}\n\n// StringArray represents a one-dimensional array of the PostgreSQL character types.\ntype StringArray []string\n\n// Scan implements the sql.Scanner interface.\nfunc (a *StringArray) Scan(src interface{}) error {\n\tswitch src := src.(type) {\n\tcase []byte:\n\t\treturn a.scanBytes(src)\n\tcase string:\n\t\treturn a.scanBytes([]byte(src))\n\tcase nil:\n\t\t*a = nil\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"pq: cannot convert %T to StringArray\", src)\n}\n\nfunc (a *StringArray) scanBytes(src []byte) error {\n\telems, err := scanLinearArray(src, []byte{','}, \"StringArray\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif *a != nil && len(elems) == 0 {\n\t\t*a = (*a)[:0]\n\t} else {\n\t\tb := make(StringArray, len(elems))\n\t\tfor i, v := range elems {\n\t\t\tif b[i] = string(v); v == nil {\n\t\t\t\treturn fmt.Errorf(\"pq: parsing array element index %d: cannot convert nil to string\", i)\n\t\t\t}\n\t\t}\n\t\t*a = b\n\t}\n\treturn nil\n}\n\n// Value implements the driver.Valuer interface.\nfunc (a StringArray) Value() (driver.Value, error) {\n\tif a == nil {\n\t\treturn nil, nil\n\t}\n\n\tif n := len(a); n > 0 {\n\t\t// There will be at least two curly brackets, 2*N bytes of quotes,\n\t\t// and N-1 bytes of delimiters.\n\t\tb := make([]byte, 1, 1+3*n)\n\t\tb[0] = '{'\n\n\t\tb = appendArrayQuotedBytes(b, []byte(a[0]))\n\t\tfor i := 1; i < n; i++ {\n\t\t\tb = append(b, ',')\n\t\t\tb = appendArrayQuotedBytes(b, []byte(a[i]))\n\t\t}\n\n\t\treturn string(append(b, '}')), nil\n\t}\n\n\treturn \"{}\", nil\n}\n\n// appendArray appends rv to the buffer, returning the extended buffer and\n// the delimiter used between elements.\n//\n// It panics when n <= 0 or rv's Kind is not reflect.Array nor reflect.Slice.\nfunc appendArray(b []byte, rv reflect.Value, n int) ([]byte, string, error) {\n\tvar del string\n\tvar err error\n\n\tb = append(b, '{')\n\n\tif b, del, err = appendArrayElement(b, rv.Index(0)); err != nil {\n\t\treturn b, del, err\n\t}\n\n\tfor i := 1; i < n; i++ {\n\t\tb = append(b, del...)\n\t\tif b, del, err = appendArrayElement(b, rv.Index(i)); err != nil {\n\t\t\treturn b, del, err\n\t\t}\n\t}\n\n\treturn append(b, '}'), del, nil\n}\n\n// appendArrayElement appends rv to the buffer, returning the extended buffer\n// and the delimiter to use before the next element.\n//\n// When rv's Kind is neither reflect.Array nor reflect.Slice, it is converted\n// using driver.DefaultParameterConverter and the resulting []byte or string\n// is double-quoted.\n//\n// See http://www.postgresql.org/docs/current/static/arrays.html#ARRAYS-IO\nfunc appendArrayElement(b []byte, rv reflect.Value) ([]byte, string, error) {\n\tif k := rv.Kind(); k == reflect.Array || k == reflect.Slice {\n\t\tif t := rv.Type(); t != typeByteSlice && !t.Implements(typeDriverValuer) {\n\t\t\tif n := rv.Len(); n > 0 {\n\t\t\t\treturn appendArray(b, rv, n)\n\t\t\t}\n\n\t\t\treturn b, \"\", nil\n\t\t}\n\t}\n\n\tvar del = \",\"\n\tvar err error\n\tvar iv interface{} = rv.Interface()\n\n\tif ad, ok := iv.(ArrayDelimiter); ok {\n\t\tdel = ad.ArrayDelimiter()\n\t}\n\n\tif iv, err = driver.DefaultParameterConverter.ConvertValue(iv); err != nil {\n\t\treturn b, del, err\n\t}\n\n\tswitch v := iv.(type) {\n\tcase nil:\n\t\treturn append(b, \"NULL\"...), del, nil\n\tcase []byte:\n\t\treturn appendArrayQuotedBytes(b, v), del, nil\n\tcase string:\n\t\treturn appendArrayQuotedBytes(b, []byte(v)), del, nil\n\t}\n\n\tb, err = appendValue(b, iv)\n\treturn b, del, err\n}\n\nfunc appendArrayQuotedBytes(b, v []byte) []byte {\n\tb = append(b, '\"')\n\tfor {\n\t\ti := bytes.IndexAny(v, `\"\\`)\n\t\tif i < 0 {\n\t\t\tb = append(b, v...)\n\t\t\tbreak\n\t\t}\n\t\tif i > 0 {\n\t\t\tb = append(b, v[:i]...)\n\t\t}\n\t\tb = append(b, '\\\\', v[i])\n\t\tv = v[i+1:]\n\t}\n\treturn append(b, '\"')\n}\n\nfunc appendValue(b []byte, v driver.Value) ([]byte, error) {\n\treturn append(b, encode(nil, v, 0)...), nil\n}\n\n// parseArray extracts the dimensions and elements of an array represented in\n// text format. Only representations emitted by the backend are supported.\n// Notably, whitespace around brackets and delimiters is significant, and NULL\n// is case-sensitive.\n//\n// See http://www.postgresql.org/docs/current/static/arrays.html#ARRAYS-IO\nfunc parseArray(src, del []byte) (dims []int, elems [][]byte, err error) {\n\tvar depth, i int\n\n\tif len(src) < 1 || src[0] != '{' {\n\t\treturn nil, nil, fmt.Errorf(\"pq: unable to parse array; expected %q at offset %d\", '{', 0)\n\t}\n\nOpen:\n\tfor i < len(src) {\n\t\tswitch src[i] {\n\t\tcase '{':\n\t\t\tdepth++\n\t\t\ti++\n\t\tcase '}':\n\t\t\telems = make([][]byte, 0)\n\t\t\tgoto Close\n\t\tdefault:\n\t\t\tbreak Open\n\t\t}\n\t}\n\tdims = make([]int, i)\n\nElement:\n\tfor i < len(src) {\n\t\tswitch src[i] {\n\t\tcase '{':\n\t\t\tif depth == len(dims) {\n\t\t\t\tbreak Element\n\t\t\t}\n\t\t\tdepth++\n\t\t\tdims[depth-1] = 0\n\t\t\ti++\n\t\tcase '\"':\n\t\t\tvar elem = []byte{}\n\t\t\tvar escape bool\n\t\t\tfor i++; i < len(src); i++ {\n\t\t\t\tif escape {\n\t\t\t\t\telem = append(elem, src[i])\n\t\t\t\t\tescape = false\n\t\t\t\t} else {\n\t\t\t\t\tswitch src[i] {\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telem = append(elem, src[i])\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\tescape = true\n\t\t\t\t\tcase '\"':\n\t\t\t\t\t\telems = append(elems, elem)\n\t\t\t\t\t\ti++\n\t\t\t\t\t\tbreak Element\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\tfor start := i; i < len(src); i++ {\n\t\t\t\tif bytes.HasPrefix(src[i:], del) || src[i] == '}' {\n\t\t\t\t\telem := src[start:i]\n\t\t\t\t\tif len(elem) == 0 {\n\t\t\t\t\t\treturn nil, nil, fmt.Errorf(\"pq: unable to parse array; unexpected %q at offset %d\", src[i], i)\n\t\t\t\t\t}\n\t\t\t\t\tif bytes.Equal(elem, []byte(\"NULL\")) {\n\t\t\t\t\t\telem = nil\n\t\t\t\t\t}\n\t\t\t\t\telems = append(elems, elem)\n\t\t\t\t\tbreak Element\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor i < len(src) {\n\t\tif bytes.HasPrefix(src[i:], del) && depth > 0 {\n\t\t\tdims[depth-1]++\n\t\t\ti += len(del)\n\t\t\tgoto Element\n\t\t} else if src[i] == '}' && depth > 0 {\n\t\t\tdims[depth-1]++\n\t\t\tdepth--\n\t\t\ti++\n\t\t} else {\n\t\t\treturn nil, nil, fmt.Errorf(\"pq: unable to parse array; unexpected %q at offset %d\", src[i], i)\n\t\t}\n\t}\n\nClose:\n\tfor i < len(src) {\n\t\tif src[i] == '}' && depth > 0 {\n\t\t\tdepth--\n\t\t\ti++\n\t\t} else {\n\t\t\treturn nil, nil, fmt.Errorf(\"pq: unable to parse array; unexpected %q at offset %d\", src[i], i)\n\t\t}\n\t}\n\tif depth > 0 {\n\t\terr = fmt.Errorf(\"pq: unable to parse array; expected %q at offset %d\", '}', i)\n\t}\n\tif err == nil {\n\t\tfor _, d := range dims {\n\t\t\tif (len(elems) % d) != 0 {\n\t\t\t\terr = fmt.Errorf(\"pq: multidimensional arrays must have elements with matching dimensions\")\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc scanLinearArray(src, del []byte, typ string) (elems [][]byte, err error) {\n\tdims, elems, err := parseArray(src, del)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(dims) > 1 {\n\t\treturn nil, fmt.Errorf(\"pq: cannot convert ARRAY%s to %s\", strings.Replace(fmt.Sprint(dims), \" \", \"][\", -1), typ)\n\t}\n\treturn elems, err\n}\n\nfunc parseBytea(s []byte) (result []byte, err error) {\n\tif len(s) >= 2 && bytes.Equal(s[:2], []byte(\"\\\\x\")) {\n\t\t// bytea_output = hex\n\t\ts = s[2:] // trim off leading \"\\\\x\"\n\t\tresult = make([]byte, hex.DecodedLen(len(s)))\n\t\t_, err := hex.Decode(result, s)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\t// bytea_output = escape\n\t\tfor len(s) > 0 {\n\t\t\tif s[0] == '\\\\' {\n\t\t\t\t// escaped '\\\\'\n\t\t\t\tif len(s) >= 2 && s[1] == '\\\\' {\n\t\t\t\t\tresult = append(result, '\\\\')\n\t\t\t\t\ts = s[2:]\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// '\\\\' followed by an octal number\n\t\t\t\tif len(s) < 4 {\n\t\t\t\t\treturn nil, fmt.Errorf(\"invalid bytea sequence %v\", s)\n\t\t\t\t}\n\t\t\t\tr, err := strconv.ParseUint(string(s[1:4]), 8, 8)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"could not parse bytea value: %s\", err.Error())\n\t\t\t\t}\n\t\t\t\tresult = append(result, byte(r))\n\t\t\t\ts = s[4:]\n\t\t\t} else {\n\t\t\t\t// We hit an unescaped, raw byte.  Try to read in as many as\n\t\t\t\t// possible in one go.\n\t\t\t\ti := bytes.IndexByte(s, '\\\\')\n\t\t\t\tif i == -1 {\n\t\t\t\t\tresult = append(result, s...)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tresult = append(result, s[:i]...)\n\t\t\t\ts = s[i:]\n\t\t\t}\n\t\t}\n\t}\n\treturn result, nil\n}\n\nfunc encode(parameterStatus *parameterStatus, x interface{}, pgtypOid Oid) []byte {\n\tswitch v := x.(type) {\n\tcase int64:\n\t\treturn strconv.AppendInt(nil, v, 10)\n\tcase float64:\n\t\treturn strconv.AppendFloat(nil, v, 'f', -1, 64)\n\tcase []byte:\n\t\tif pgtypOid == T_bytea {\n\t\t\treturn encodeBytea(parameterStatus.serverVersion, v)\n\t\t}\n\n\t\treturn v\n\tcase string:\n\t\tif pgtypOid == T_bytea {\n\t\t\treturn encodeBytea(parameterStatus.serverVersion, []byte(v))\n\t\t}\n\n\t\treturn []byte(v)\n\tcase bool:\n\t\treturn strconv.AppendBool(nil, v)\n\tcase time.Time:\n\t\treturn formatTs(v)\n\n\tdefault:\n\t\tpanic(fmt.Errorf(\"pq: %s\", fmt.Sprintf(\"encode: unknown type for %T\", v)))\n\t}\n}\n\ntype parameterStatus struct {\n\t// server version in the same format as server_version_num, or 0 if\n\t// unavailable\n\tserverVersion int\n\n\t// the current location based on the TimeZone value of the session, if\n\t// available\n\tcurrentLocation *time.Location\n}\n\nfunc encodeBytea(serverVersion int, v []byte) (result []byte) {\n\tif serverVersion >= 90000 {\n\t\t// Use the hex format if we know that the server supports it\n\t\tresult = make([]byte, 2+hex.EncodedLen(len(v)))\n\t\tresult[0] = '\\\\'\n\t\tresult[1] = 'x'\n\t\thex.Encode(result[2:], v)\n\t} else {\n\t\t// .. or resort to \"escape\"\n\t\tfor _, b := range v {\n\t\t\tif b == '\\\\' {\n\t\t\t\tresult = append(result, '\\\\', '\\\\')\n\t\t\t} else if b < 0x20 || b > 0x7e {\n\t\t\t\tresult = append(result, []byte(fmt.Sprintf(\"\\\\%03o\", b))...)\n\t\t\t} else {\n\t\t\t\tresult = append(result, b)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result\n}\n\nvar infinityTsEnabled = false\nvar infinityTsNegative time.Time\nvar infinityTsPositive time.Time\n\n// formatTs formats t into a format postgres understands.\nfunc formatTs(t time.Time) []byte {\n\tif infinityTsEnabled {\n\t\t// t <= -infinity : ! (t > -infinity)\n\t\tif !t.After(infinityTsNegative) {\n\t\t\treturn []byte(\"-infinity\")\n\t\t}\n\t\t// t >= infinity : ! (!t < infinity)\n\t\tif !t.Before(infinityTsPositive) {\n\t\t\treturn []byte(\"infinity\")\n\t\t}\n\t}\n\treturn FormatTimestamp(t)\n}\n\n// FormatTimestamp formats t into Postgres' text format for timestamps.\nfunc FormatTimestamp(t time.Time) []byte {\n\t// Need to send dates before 0001 A.D. with \" BC\" suffix, instead of the\n\t// minus sign preferred by Go.\n\t// Beware, \"0000\" in ISO is \"1 BC\", \"-0001\" is \"2 BC\" and so on\n\tbc := false\n\tif t.Year() <= 0 {\n\t\t// flip year sign, and add 1, e.g: \"0\" will be \"1\", and \"-10\" will be \"11\"\n\t\tt = t.AddDate((-t.Year())*2+1, 0, 0)\n\t\tbc = true\n\t}\n\tb := []byte(t.Format(\"2006-01-02 15:04:05.999999999Z07:00\"))\n\n\t_, offset := t.Zone()\n\toffset %= 60\n\tif offset != 0 {\n\t\t// RFC3339Nano already printed the minus sign\n\t\tif offset < 0 {\n\t\t\toffset = -offset\n\t\t}\n\n\t\tb = append(b, ':')\n\t\tif offset < 10 {\n\t\t\tb = append(b, '0')\n\t\t}\n\t\tb = strconv.AppendInt(b, int64(offset), 10)\n\t}\n\n\tif bc {\n\t\tb = append(b, \" BC\"...)\n\t}\n\treturn b\n}\n\n// Oid is a Postgres Object ID.\ntype Oid uint32\n\nconst (\n\tT_bool             Oid = 16\n\tT_bytea            Oid = 17\n\tT_char             Oid = 18\n\tT_name             Oid = 19\n\tT_int8             Oid = 20\n\tT_int2             Oid = 21\n\tT_int2vector       Oid = 22\n\tT_int4             Oid = 23\n\tT_regproc          Oid = 24\n\tT_text             Oid = 25\n\tT_oid              Oid = 26\n\tT_tid              Oid = 27\n\tT_xid              Oid = 28\n\tT_cid              Oid = 29\n\tT_oidvector        Oid = 30\n\tT_pg_ddl_command   Oid = 32\n\tT_pg_type          Oid = 71\n\tT_pg_attribute     Oid = 75\n\tT_pg_proc          Oid = 81\n\tT_pg_class         Oid = 83\n\tT_json             Oid = 114\n\tT_xml              Oid = 142\n\tT__xml             Oid = 143\n\tT_pg_node_tree     Oid = 194\n\tT__json            Oid = 199\n\tT_smgr             Oid = 210\n\tT_index_am_handler Oid = 325\n\tT_point            Oid = 600\n\tT_lseg             Oid = 601\n\tT_path             Oid = 602\n\tT_box              Oid = 603\n\tT_polygon          Oid = 604\n\tT_line             Oid = 628\n\tT__line            Oid = 629\n\tT_cidr             Oid = 650\n\tT__cidr            Oid = 651\n\tT_float4           Oid = 700\n\tT_float8           Oid = 701\n\tT_abstime          Oid = 702\n\tT_reltime          Oid = 703\n\tT_tinterval        Oid = 704\n\tT_unknown          Oid = 705\n\tT_circle           Oid = 718\n\tT__circle          Oid = 719\n\tT_money            Oid = 790\n\tT__money           Oid = 791\n\tT_macaddr          Oid = 829\n\tT_inet             Oid = 869\n\tT__bool            Oid = 1000\n\tT__bytea           Oid = 1001\n\tT__char            Oid = 1002\n\tT__name            Oid = 1003\n\tT__int2            Oid = 1005\n\tT__int2vector      Oid = 1006\n\tT__int4            Oid = 1007\n\tT__regproc         Oid = 1008\n\tT__text            Oid = 1009\n\tT__tid             Oid = 1010\n\tT__xid             Oid = 1011\n\tT__cid             Oid = 1012\n\tT__oidvector       Oid = 1013\n\tT__bpchar          Oid = 1014\n\tT__varchar         Oid = 1015\n\tT__int8            Oid = 1016\n\tT__point           Oid = 1017\n\tT__lseg            Oid = 1018\n\tT__path            Oid = 1019\n\tT__box             Oid = 1020\n\tT__float4          Oid = 1021\n\tT__float8          Oid = 1022\n\tT__abstime         Oid = 1023\n\tT__reltime         Oid = 1024\n\tT__tinterval       Oid = 1025\n\tT__polygon         Oid = 1027\n\tT__oid             Oid = 1028\n\tT_aclitem          Oid = 1033\n\tT__aclitem         Oid = 1034\n\tT__macaddr         Oid = 1040\n\tT__inet            Oid = 1041\n\tT_bpchar           Oid = 1042\n\tT_varchar          Oid = 1043\n\tT_date             Oid = 1082\n\tT_time             Oid = 1083\n\tT_timestamp        Oid = 1114\n\tT__timestamp       Oid = 1115\n\tT__date            Oid = 1182\n\tT__time            Oid = 1183\n\tT_timestamptz      Oid = 1184\n\tT__timestamptz     Oid = 1185\n\tT_interval         Oid = 1186\n\tT__interval        Oid = 1187\n\tT__numeric         Oid = 1231\n\tT_pg_database      Oid = 1248\n\tT__cstring         Oid = 1263\n\tT_timetz           Oid = 1266\n\tT__timetz          Oid = 1270\n\tT_bit              Oid = 1560\n\tT__bit             Oid = 1561\n\tT_varbit           Oid = 1562\n\tT__varbit          Oid = 1563\n\tT_numeric          Oid = 1700\n\tT_refcursor        Oid = 1790\n\tT__refcursor       Oid = 2201\n\tT_regprocedure     Oid = 2202\n\tT_regoper          Oid = 2203\n\tT_regoperator      Oid = 2204\n\tT_regclass         Oid = 2205\n\tT_regtype          Oid = 2206\n\tT__regprocedure    Oid = 2207\n\tT__regoper         Oid = 2208\n\tT__regoperator     Oid = 2209\n\tT__regclass        Oid = 2210\n\tT__regtype         Oid = 2211\n\tT_record           Oid = 2249\n\tT_cstring          Oid = 2275\n\tT_any              Oid = 2276\n\tT_anyarray         Oid = 2277\n\tT_void             Oid = 2278\n\tT_trigger          Oid = 2279\n\tT_language_handler Oid = 2280\n\tT_internal         Oid = 2281\n\tT_opaque           Oid = 2282\n\tT_anyelement       Oid = 2283\n\tT__record          Oid = 2287\n\tT_anynonarray      Oid = 2776\n\tT_pg_authid        Oid = 2842\n\tT_pg_auth_members  Oid = 2843\n\tT__txid_snapshot   Oid = 2949\n\tT_uuid             Oid = 2950\n\tT__uuid            Oid = 2951\n\tT_txid_snapshot    Oid = 2970\n\tT_fdw_handler      Oid = 3115\n\tT_pg_lsn           Oid = 3220\n\tT__pg_lsn          Oid = 3221\n\tT_tsm_handler      Oid = 3310\n\tT_anyenum          Oid = 3500\n\tT_tsvector         Oid = 3614\n\tT_tsquery          Oid = 3615\n\tT_gtsvector        Oid = 3642\n\tT__tsvector        Oid = 3643\n\tT__gtsvector       Oid = 3644\n\tT__tsquery         Oid = 3645\n\tT_regconfig        Oid = 3734\n\tT__regconfig       Oid = 3735\n\tT_regdictionary    Oid = 3769\n\tT__regdictionary   Oid = 3770\n\tT_jsonb            Oid = 3802\n\tT__jsonb           Oid = 3807\n\tT_anyrange         Oid = 3831\n\tT_event_trigger    Oid = 3838\n\tT_int4range        Oid = 3904\n\tT__int4range       Oid = 3905\n\tT_numrange         Oid = 3906\n\tT__numrange        Oid = 3907\n\tT_tsrange          Oid = 3908\n\tT__tsrange         Oid = 3909\n\tT_tstzrange        Oid = 3910\n\tT__tstzrange       Oid = 3911\n\tT_daterange        Oid = 3912\n\tT__daterange       Oid = 3913\n\tT_int8range        Oid = 3926\n\tT__int8range       Oid = 3927\n\tT_pg_shseclabel    Oid = 4066\n\tT_regnamespace     Oid = 4089\n\tT__regnamespace    Oid = 4090\n\tT_regrole          Oid = 4096\n\tT__regrole         Oid = 4097\n)\n\nvar TypeName = map[Oid]string{\n\tT_bool:             \"BOOL\",\n\tT_bytea:            \"BYTEA\",\n\tT_char:             \"CHAR\",\n\tT_name:             \"NAME\",\n\tT_int8:             \"INT8\",\n\tT_int2:             \"INT2\",\n\tT_int2vector:       \"INT2VECTOR\",\n\tT_int4:             \"INT4\",\n\tT_regproc:          \"REGPROC\",\n\tT_text:             \"TEXT\",\n\tT_oid:              \"OID\",\n\tT_tid:              \"TID\",\n\tT_xid:              \"XID\",\n\tT_cid:              \"CID\",\n\tT_oidvector:        \"OIDVECTOR\",\n\tT_pg_ddl_command:   \"PG_DDL_COMMAND\",\n\tT_pg_type:          \"PG_TYPE\",\n\tT_pg_attribute:     \"PG_ATTRIBUTE\",\n\tT_pg_proc:          \"PG_PROC\",\n\tT_pg_class:         \"PG_CLASS\",\n\tT_json:             \"JSON\",\n\tT_xml:              \"XML\",\n\tT__xml:             \"_XML\",\n\tT_pg_node_tree:     \"PG_NODE_TREE\",\n\tT__json:            \"_JSON\",\n\tT_smgr:             \"SMGR\",\n\tT_index_am_handler: \"INDEX_AM_HANDLER\",\n\tT_point:            \"POINT\",\n\tT_lseg:             \"LSEG\",\n\tT_path:             \"PATH\",\n\tT_box:              \"BOX\",\n\tT_polygon:          \"POLYGON\",\n\tT_line:             \"LINE\",\n\tT__line:            \"_LINE\",\n\tT_cidr:             \"CIDR\",\n\tT__cidr:            \"_CIDR\",\n\tT_float4:           \"FLOAT4\",\n\tT_float8:           \"FLOAT8\",\n\tT_abstime:          \"ABSTIME\",\n\tT_reltime:          \"RELTIME\",\n\tT_tinterval:        \"TINTERVAL\",\n\tT_unknown:          \"UNKNOWN\",\n\tT_circle:           \"CIRCLE\",\n\tT__circle:          \"_CIRCLE\",\n\tT_money:            \"MONEY\",\n\tT__money:           \"_MONEY\",\n\tT_macaddr:          \"MACADDR\",\n\tT_inet:             \"INET\",\n\tT__bool:            \"_BOOL\",\n\tT__bytea:           \"_BYTEA\",\n\tT__char:            \"_CHAR\",\n\tT__name:            \"_NAME\",\n\tT__int2:            \"_INT2\",\n\tT__int2vector:      \"_INT2VECTOR\",\n\tT__int4:            \"_INT4\",\n\tT__regproc:         \"_REGPROC\",\n\tT__text:            \"_TEXT\",\n\tT__tid:             \"_TID\",\n\tT__xid:             \"_XID\",\n\tT__cid:             \"_CID\",\n\tT__oidvector:       \"_OIDVECTOR\",\n\tT__bpchar:          \"_BPCHAR\",\n\tT__varchar:         \"_VARCHAR\",\n\tT__int8:            \"_INT8\",\n\tT__point:           \"_POINT\",\n\tT__lseg:            \"_LSEG\",\n\tT__path:            \"_PATH\",\n\tT__box:             \"_BOX\",\n\tT__float4:          \"_FLOAT4\",\n\tT__float8:          \"_FLOAT8\",\n\tT__abstime:         \"_ABSTIME\",\n\tT__reltime:         \"_RELTIME\",\n\tT__tinterval:       \"_TINTERVAL\",\n\tT__polygon:         \"_POLYGON\",\n\tT__oid:             \"_OID\",\n\tT_aclitem:          \"ACLITEM\",\n\tT__aclitem:         \"_ACLITEM\",\n\tT__macaddr:         \"_MACADDR\",\n\tT__inet:            \"_INET\",\n\tT_bpchar:           \"BPCHAR\",\n\tT_varchar:          \"VARCHAR\",\n\tT_date:             \"DATE\",\n\tT_time:             \"TIME\",\n\tT_timestamp:        \"TIMESTAMP\",\n\tT__timestamp:       \"_TIMESTAMP\",\n\tT__date:            \"_DATE\",\n\tT__time:            \"_TIME\",\n\tT_timestamptz:      \"TIMESTAMPTZ\",\n\tT__timestamptz:     \"_TIMESTAMPTZ\",\n\tT_interval:         \"INTERVAL\",\n\tT__interval:        \"_INTERVAL\",\n\tT__numeric:         \"_NUMERIC\",\n\tT_pg_database:      \"PG_DATABASE\",\n\tT__cstring:         \"_CSTRING\",\n\tT_timetz:           \"TIMETZ\",\n\tT__timetz:          \"_TIMETZ\",\n\tT_bit:              \"BIT\",\n\tT__bit:             \"_BIT\",\n\tT_varbit:           \"VARBIT\",\n\tT__varbit:          \"_VARBIT\",\n\tT_numeric:          \"NUMERIC\",\n\tT_refcursor:        \"REFCURSOR\",\n\tT__refcursor:       \"_REFCURSOR\",\n\tT_regprocedure:     \"REGPROCEDURE\",\n\tT_regoper:          \"REGOPER\",\n\tT_regoperator:      \"REGOPERATOR\",\n\tT_regclass:         \"REGCLASS\",\n\tT_regtype:          \"REGTYPE\",\n\tT__regprocedure:    \"_REGPROCEDURE\",\n\tT__regoper:         \"_REGOPER\",\n\tT__regoperator:     \"_REGOPERATOR\",\n\tT__regclass:        \"_REGCLASS\",\n\tT__regtype:         \"_REGTYPE\",\n\tT_record:           \"RECORD\",\n\tT_cstring:          \"CSTRING\",\n\tT_any:              \"ANY\",\n\tT_anyarray:         \"ANYARRAY\",\n\tT_void:             \"VOID\",\n\tT_trigger:          \"TRIGGER\",\n\tT_language_handler: \"LANGUAGE_HANDLER\",\n\tT_internal:         \"INTERNAL\",\n\tT_opaque:           \"OPAQUE\",\n\tT_anyelement:       \"ANYELEMENT\",\n\tT__record:          \"_RECORD\",\n\tT_anynonarray:      \"ANYNONARRAY\",\n\tT_pg_authid:        \"PG_AUTHID\",\n\tT_pg_auth_members:  \"PG_AUTH_MEMBERS\",\n\tT__txid_snapshot:   \"_TXID_SNAPSHOT\",\n\tT_uuid:             \"UUID\",\n\tT__uuid:            \"_UUID\",\n\tT_txid_snapshot:    \"TXID_SNAPSHOT\",\n\tT_fdw_handler:      \"FDW_HANDLER\",\n\tT_pg_lsn:           \"PG_LSN\",\n\tT__pg_lsn:          \"_PG_LSN\",\n\tT_tsm_handler:      \"TSM_HANDLER\",\n\tT_anyenum:          \"ANYENUM\",\n\tT_tsvector:         \"TSVECTOR\",\n\tT_tsquery:          \"TSQUERY\",\n\tT_gtsvector:        \"GTSVECTOR\",\n\tT__tsvector:        \"_TSVECTOR\",\n\tT__gtsvector:       \"_GTSVECTOR\",\n\tT__tsquery:         \"_TSQUERY\",\n\tT_regconfig:        \"REGCONFIG\",\n\tT__regconfig:       \"_REGCONFIG\",\n\tT_regdictionary:    \"REGDICTIONARY\",\n\tT__regdictionary:   \"_REGDICTIONARY\",\n\tT_jsonb:            \"JSONB\",\n\tT__jsonb:           \"_JSONB\",\n\tT_anyrange:         \"ANYRANGE\",\n\tT_event_trigger:    \"EVENT_TRIGGER\",\n\tT_int4range:        \"INT4RANGE\",\n\tT__int4range:       \"_INT4RANGE\",\n\tT_numrange:         \"NUMRANGE\",\n\tT__numrange:        \"_NUMRANGE\",\n\tT_tsrange:          \"TSRANGE\",\n\tT__tsrange:         \"_TSRANGE\",\n\tT_tstzrange:        \"TSTZRANGE\",\n\tT__tstzrange:       \"_TSTZRANGE\",\n\tT_daterange:        \"DATERANGE\",\n\tT__daterange:       \"_DATERANGE\",\n\tT_int8range:        \"INT8RANGE\",\n\tT__int8range:       \"_INT8RANGE\",\n\tT_pg_shseclabel:    \"PG_SHSECLABEL\",\n\tT_regnamespace:     \"REGNAMESPACE\",\n\tT__regnamespace:    \"_REGNAMESPACE\",\n\tT_regrole:          \"REGROLE\",\n\tT__regrole:         \"_REGROLE\",\n}\n"
  },
  {
    "path": "internal/testutil/testutil.go",
    "content": "package testutil\n\nimport (\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n)\n\nfunc Diff[T any](got, want T) string {\n\tdiff := cmp.Diff(\n\t\tgot, want,\n\t\tcmp.Exporter(func(typ reflect.Type) bool { return true }),\n\t\tcmpopts.EquateEmpty(),\n\t)\n\tif diff != \"\" {\n\t\treturn \"\\n-got +want\\n\" + diff\n\t}\n\treturn \"\"\n}\n\nfunc Callers() string {\n\tvar pc [50]uintptr\n\tn := runtime.Callers(2, pc[:]) // skip runtime.Callers + Callers\n\tcallsites := make([]string, 0, n)\n\tframes := runtime.CallersFrames(pc[:n])\n\tfor frame, more := frames.Next(); more; frame, more = frames.Next() {\n\t\tcallsites = append(callsites, frame.File+\":\"+strconv.Itoa(frame.Line))\n\t}\n\tcallsites = callsites[:len(callsites)-1] // skip testing.tRunner\n\tif len(callsites) == 1 {\n\t\treturn \"\"\n\t}\n\tvar b strings.Builder\n\tb.WriteString(\"\\n[\")\n\tfor i := len(callsites) - 1; i >= 0; i-- {\n\t\tif i < len(callsites)-1 {\n\t\t\tb.WriteString(\" -> \")\n\t\t}\n\t\tb.WriteString(filepath.Base(callsites[i]))\n\t}\n\tb.WriteString(\"]\")\n\treturn b.String()\n}\n"
  },
  {
    "path": "joins.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n)\n\n// Join operators.\nconst (\n\tJoinInner = \"JOIN\"\n\tJoinLeft  = \"LEFT JOIN\"\n\tJoinRight = \"RIGHT JOIN\"\n\tJoinFull  = \"FULL JOIN\"\n\tJoinCross = \"CROSS JOIN\"\n)\n\n// JoinTable represents a join on a table.\ntype JoinTable struct {\n\tJoinOperator string\n\tTable        Table\n\tOnPredicate  Predicate\n\tUsingFields  []Field\n}\n\n// JoinUsing creates a new JoinTable with the USING operator.\nfunc JoinUsing(table Table, fields ...Field) JoinTable {\n\treturn JoinTable{JoinOperator: JoinInner, Table: table, UsingFields: fields}\n}\n\n// Join creates a new JoinTable with the JOIN operator.\nfunc Join(table Table, predicates ...Predicate) JoinTable {\n\treturn CustomJoin(JoinInner, table, predicates...)\n}\n\n// LeftJoin creates a new JoinTable with the LEFT JOIN operator.\nfunc LeftJoin(table Table, predicates ...Predicate) JoinTable {\n\treturn CustomJoin(JoinLeft, table, predicates...)\n}\n\n// FullJoin creates a new JoinTable with the FULL JOIN operator.\nfunc FullJoin(table Table, predicates ...Predicate) JoinTable {\n\treturn CustomJoin(JoinFull, table, predicates...)\n}\n\n// CrossJoin creates a new JoinTable with the CROSS JOIN operator.\nfunc CrossJoin(table Table) JoinTable {\n\treturn CustomJoin(JoinCross, table)\n}\n\n// CustomJoin creates a new JoinTable with the a custom join operator.\nfunc CustomJoin(joinOperator string, table Table, predicates ...Predicate) JoinTable {\n\tswitch len(predicates) {\n\tcase 0:\n\t\treturn JoinTable{JoinOperator: joinOperator, Table: table}\n\tcase 1:\n\t\treturn JoinTable{JoinOperator: joinOperator, Table: table, OnPredicate: predicates[0]}\n\tdefault:\n\t\treturn JoinTable{JoinOperator: joinOperator, Table: table, OnPredicate: And(predicates...)}\n\t}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (join JoinTable) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tif join.JoinOperator == \"\" {\n\t\tjoin.JoinOperator = JoinInner\n\t}\n\tvariadicPredicate, isVariadic := join.OnPredicate.(VariadicPredicate)\n\thasNoPredicate := join.OnPredicate == nil && len(variadicPredicate.Predicates) == 0 && len(join.UsingFields) == 0\n\tif hasNoPredicate && (join.JoinOperator == JoinInner ||\n\t\tjoin.JoinOperator == JoinLeft ||\n\t\tjoin.JoinOperator == JoinRight ||\n\t\tjoin.JoinOperator == JoinFull) &&\n\t\t// exclude sqlite from this check because they allow join without predicate\n\t\tdialect != DialectSQLite {\n\t\treturn fmt.Errorf(\"%s requires at least one predicate specified\", join.JoinOperator)\n\t}\n\tif dialect == DialectSQLite && (join.JoinOperator == JoinRight || join.JoinOperator == JoinFull) {\n\t\treturn fmt.Errorf(\"sqlite does not support %s\", join.JoinOperator)\n\t}\n\n\t// JOIN\n\tbuf.WriteString(string(join.JoinOperator) + \" \")\n\tif join.Table == nil {\n\t\treturn fmt.Errorf(\"joining on a nil table\")\n\t}\n\n\t// <table>\n\t_, isQuery := join.Table.(Query)\n\tif isQuery {\n\t\tbuf.WriteString(\"(\")\n\t}\n\terr := join.Table.WriteSQL(ctx, dialect, buf, args, params)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif isQuery {\n\t\tbuf.WriteString(\")\")\n\t}\n\n\t// AS\n\tif tableAlias := getAlias(join.Table); tableAlias != \"\" {\n\t\tbuf.WriteString(\" AS \" + QuoteIdentifier(dialect, tableAlias) + quoteTableColumns(dialect, join.Table))\n\t} else if isQuery && dialect != DialectSQLite {\n\t\treturn fmt.Errorf(\"%s %s subquery must have alias\", dialect, join.JoinOperator)\n\t}\n\n\tif isVariadic {\n\t\t// ON VariadicPredicate\n\t\tbuf.WriteString(\" ON \")\n\t\tvariadicPredicate.Toplevel = true\n\t\terr = variadicPredicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else if join.OnPredicate != nil {\n\t\t// ON Predicate\n\t\tbuf.WriteString(\" ON \")\n\t\terr = join.OnPredicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else if len(join.UsingFields) > 0 {\n\t\t// USING Fields\n\t\tbuf.WriteString(\" USING (\")\n\t\terr = writeFieldsWithPrefix(ctx, dialect, buf, args, params, join.UsingFields, \"\", false)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tbuf.WriteString(\")\")\n\t}\n\treturn nil\n}\n\nfunc writeJoinTables(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, joinTables []JoinTable) error {\n\tvar err error\n\tfor i, joinTable := range joinTables {\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\" \")\n\t\t}\n\t\terr = joinTable.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"join #%d: %w\", i+1, err)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "joins_test.go",
    "content": "package sq\n\nimport \"testing\"\n\nfunc TestJoinTables(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\ttests := []TestTable{{\n\t\tdescription: \"JoinUsing\",\n\t\titem:        JoinUsing(a, a.FIRST_NAME, a.LAST_NAME),\n\t\twantQuery:   \"JOIN actor AS a USING (first_name, last_name)\",\n\t}, {\n\t\tdescription: \"Join without operator\",\n\t\titem:        CustomJoin(\"\", a, a.ACTOR_ID.Eq(a.ACTOR_ID), a.FIRST_NAME.Ne(a.LAST_NAME)),\n\t\twantQuery:   \"JOIN actor AS a ON a.actor_id = a.actor_id AND a.first_name <> a.last_name\",\n\t}, {\n\t\tdescription: \"Join\",\n\t\titem:        Join(a, a.ACTOR_ID.Eq(a.ACTOR_ID)),\n\t\twantQuery:   \"JOIN actor AS a ON a.actor_id = a.actor_id\",\n\t}, {\n\t\tdescription: \"LeftJoin\",\n\t\titem:        LeftJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)),\n\t\twantQuery:   \"LEFT JOIN actor AS a ON a.actor_id = a.actor_id\",\n\t}, {\n\t\tdescription: \"Right Join\",\n\t\titem:        JoinTable{JoinOperator: JoinRight, Table: a, OnPredicate: a.ACTOR_ID.Eq(a.ACTOR_ID)},\n\t\twantQuery:   \"RIGHT JOIN actor AS a ON a.actor_id = a.actor_id\",\n\t}, {\n\t\tdescription: \"FullJoin\",\n\t\titem:        FullJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)),\n\t\twantQuery:   \"FULL JOIN actor AS a ON a.actor_id = a.actor_id\",\n\t}, {\n\t\tdescription: \"CrossJoin\",\n\t\titem:        CrossJoin(a),\n\t\twantQuery:   \"CROSS JOIN actor AS a\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n\n\tnotOKTests := []TestTable{{\n\t\tdescription: \"full join has no predicate\",\n\t\titem:        FullJoin(a),\n\t}, {\n\t\tdescription: \"sqlite does not support full join\",\n\t\tdialect:     DialectSQLite,\n\t\titem:        FullJoin(a, Expr(\"TRUE\")),\n\t}, {\n\t\tdescription: \"table is nil\",\n\t\titem:        Join(nil, Expr(\"TRUE\")),\n\t}, {\n\t\tdescription: \"UsingField returns err\",\n\t\titem:        JoinUsing(a, nil),\n\t}}\n\n\tfor _, tt := range notOKTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertNotOK(t)\n\t\t})\n\t}\n\n\terrTests := []TestTable{{\n\t\tdescription: \"table err\",\n\t\titem:        Join(FaultySQL{}, a.ACTOR_ID.Eq(a.ACTOR_ID)),\n\t}, {\n\t\tdescription: \"VariadicPredicate err\",\n\t\titem:        Join(a, And(FaultySQL{})),\n\t}, {\n\t\tdescription: \"predicate err\",\n\t\titem:        Join(a, FaultySQL{}),\n\t}}\n\n\tfor _, tt := range errTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertErr(t, ErrFaultySQL)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "logger.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\n// QueryStats represents the statistics from running a query.\ntype QueryStats struct {\n\t// Dialect of the query.\n\tDialect string\n\n\t// Query string.\n\tQuery string\n\n\t// Args slice provided with the query string.\n\tArgs []any\n\n\t// Params maps param names back to arguments in the args slice (by index).\n\tParams map[string][]int\n\n\t// Err is the error from running the query.\n\tErr error\n\n\t// RowCount from running the query. Not valid for Exec().\n\tRowCount sql.NullInt64\n\n\t// RowsAffected by running the query. Not valid for\n\t// FetchOne/FetchAll/FetchCursor.\n\tRowsAffected sql.NullInt64\n\n\t// LastInsertId of the query.\n\tLastInsertId sql.NullInt64\n\n\t// Exists is the result of FetchExists().\n\tExists sql.NullBool\n\n\t// When the query started at.\n\tStartedAt time.Time\n\n\t// Time taken by the query.\n\tTimeTaken time.Duration\n\n\t// The caller file where the query was invoked.\n\tCallerFile string\n\n\t// The line in the caller file that invoked the query.\n\tCallerLine int\n\n\t// The name of the function where the query was invoked.\n\tCallerFunction string\n\n\t// The results from running the query (if it was provided).\n\tResults string\n}\n\n// LogSettings are the various log settings taken into account when producing\n// the QueryStats.\ntype LogSettings struct {\n\t// Dispatch logging asynchronously (logs may arrive out of order which can be confusing, but it won't block function calls).\n\tLogAsynchronously bool\n\n\t// Include time taken by the query.\n\tIncludeTime bool\n\n\t// Include caller (filename and line number).\n\tIncludeCaller bool\n\n\t// Include fetched results.\n\tIncludeResults int\n}\n\n// SqLogger represents a logger for the sq package.\ntype SqLogger interface {\n\t// SqLogSettings should populate a LogSettings struct, which influences\n\t// what is added into the QueryStats.\n\tSqLogSettings(context.Context, *LogSettings)\n\n\t// SqLogQuery logs a query when for the given QueryStats.\n\tSqLogQuery(context.Context, QueryStats)\n}\n\ntype sqLogger struct {\n\tlogger *log.Logger\n\tconfig LoggerConfig\n}\n\n// LoggerConfig is the config used for the sq logger.\ntype LoggerConfig struct {\n\t// Dispatch logging asynchronously (logs may arrive out of order which can be confusing, but it won't block function calls).\n\tLogAsynchronously bool\n\n\t// Show time taken by the query.\n\tShowTimeTaken bool\n\n\t// Show caller (filename and line number).\n\tShowCaller bool\n\n\t// Show fetched results.\n\tShowResults int\n\n\t// If true, logs are shown as plaintext (no color).\n\tNoColor bool\n\n\t// Verbose query interpolation, which shows the query before and after\n\t// interpolating query arguments. The logged query is interpolated by\n\t// default, InterpolateVerbose only controls whether the query before\n\t// interpolation is shown. To disable query interpolation entirely, look at\n\t// HideArgs.\n\tInterpolateVerbose bool\n\n\t// Explicitly hides arguments when logging the query (only the query\n\t// placeholders will be shown).\n\tHideArgs bool\n}\n\nvar _ SqLogger = (*sqLogger)(nil)\n\nvar defaultLogger = NewLogger(os.Stdout, \"\", log.LstdFlags, LoggerConfig{\n\tShowTimeTaken: true,\n\tShowCaller:    true,\n})\n\nvar verboseLogger = NewLogger(os.Stdout, \"\", log.LstdFlags, LoggerConfig{\n\tShowTimeTaken:      true,\n\tShowCaller:         true,\n\tShowResults:        5,\n\tInterpolateVerbose: true,\n})\n\n// NewLogger returns a new SqLogger.\nfunc NewLogger(w io.Writer, prefix string, flag int, config LoggerConfig) SqLogger {\n\treturn &sqLogger{\n\t\tlogger: log.New(w, prefix, flag),\n\t\tconfig: config,\n\t}\n}\n\n// SqLogSettings implements the SqLogger interface.\nfunc (l *sqLogger) SqLogSettings(ctx context.Context, settings *LogSettings) {\n\tsettings.LogAsynchronously = l.config.LogAsynchronously\n\tsettings.IncludeTime = l.config.ShowTimeTaken\n\tsettings.IncludeCaller = l.config.ShowCaller\n\tsettings.IncludeResults = l.config.ShowResults\n}\n\n// SqLogQuery implements the SqLogger interface.\nfunc (l *sqLogger) SqLogQuery(ctx context.Context, queryStats QueryStats) {\n\tvar reset, red, green, blue, purple string\n\tenvNoColor, _ := strconv.ParseBool(os.Getenv(\"NO_COLOR\"))\n\tif !l.config.NoColor && !envNoColor {\n\t\treset = colorReset\n\t\tred = colorRed\n\t\tgreen = colorGreen\n\t\tblue = colorBlue\n\t\tpurple = colorPurple\n\t}\n\tbuf := bufpool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer bufpool.Put(buf)\n\tif queryStats.Err == nil {\n\t\tbuf.WriteString(green + \"[OK]\" + reset)\n\t} else {\n\t\tbuf.WriteString(red + \"[FAIL]\" + reset)\n\t}\n\tif l.config.HideArgs {\n\t\tbuf.WriteString(\" \" + queryStats.Query + \";\")\n\t} else if !l.config.InterpolateVerbose {\n\t\tif queryStats.Err != nil {\n\t\t\tbuf.WriteString(\" \" + queryStats.Query + \";\")\n\t\t\tif len(queryStats.Args) > 0 {\n\t\t\t\tbuf.WriteString(\" [\")\n\t\t\t}\n\t\t\tfor i := 0; i < len(queryStats.Args); i++ {\n\t\t\t\tif i > 0 {\n\t\t\t\t\tbuf.WriteString(\", \")\n\t\t\t\t}\n\t\t\t\tbuf.WriteString(fmt.Sprintf(\"%#v\", queryStats.Args[i]))\n\t\t\t}\n\t\t\tif len(queryStats.Args) > 0 {\n\t\t\t\tbuf.WriteString(\"]\")\n\t\t\t}\n\t\t} else {\n\t\t\tquery, err := Sprintf(queryStats.Dialect, queryStats.Query, queryStats.Args)\n\t\t\tif err != nil {\n\t\t\t\tquery += \" \" + err.Error()\n\t\t\t}\n\t\t\tbuf.WriteString(\" \" + query + \";\")\n\t\t}\n\t}\n\tif queryStats.Err != nil {\n\t\terrStr := queryStats.Err.Error()\n\t\tif i := strings.IndexByte(errStr, '\\n'); i < 0 {\n\t\t\tbuf.WriteString(blue + \" err\" + reset + \"={\" + queryStats.Err.Error() + \"}\")\n\t\t}\n\t}\n\tif l.config.ShowTimeTaken {\n\t\tbuf.WriteString(blue + \" timeTaken\" + reset + \"=\" + queryStats.TimeTaken.String())\n\t}\n\tif queryStats.RowCount.Valid {\n\t\tbuf.WriteString(blue + \" rowCount\" + reset + \"=\" + strconv.FormatInt(queryStats.RowCount.Int64, 10))\n\t}\n\tif queryStats.RowsAffected.Valid {\n\t\tbuf.WriteString(blue + \" rowsAffected\" + reset + \"=\" + strconv.FormatInt(queryStats.RowsAffected.Int64, 10))\n\t}\n\tif queryStats.LastInsertId.Valid {\n\t\tbuf.WriteString(blue + \" lastInsertId\" + reset + \"=\" + strconv.FormatInt(queryStats.LastInsertId.Int64, 10))\n\t}\n\tif queryStats.Exists.Valid {\n\t\tbuf.WriteString(blue + \" exists\" + reset + \"=\" + strconv.FormatBool(queryStats.Exists.Bool))\n\t}\n\tif l.config.ShowCaller {\n\t\tbuf.WriteString(blue + \" caller\" + reset + \"=\" + queryStats.CallerFile + \":\" + strconv.Itoa(queryStats.CallerLine) + \":\" + filepath.Base(queryStats.CallerFunction))\n\t}\n\tif !l.config.HideArgs && l.config.InterpolateVerbose {\n\t\tbuf.WriteString(\"\\n\" + purple + \"----[ Executing query ]----\" + reset)\n\t\tbuf.WriteString(\"\\n\" + queryStats.Query + \"; \" + fmt.Sprintf(\"%#v\", queryStats.Args))\n\t\tbuf.WriteString(\"\\n\" + purple + \"----[ with bind values ]----\" + reset)\n\t\tquery, err := Sprintf(queryStats.Dialect, queryStats.Query, queryStats.Args)\n\t\tquery += \";\"\n\t\tif err != nil {\n\t\t\tquery += \" \" + err.Error()\n\t\t}\n\t\tbuf.WriteString(\"\\n\" + query)\n\t}\n\tif l.config.ShowResults > 0 && queryStats.Err == nil {\n\t\tbuf.WriteString(\"\\n\" + purple + \"----[ Fetched result ]----\" + reset)\n\t\tbuf.WriteString(queryStats.Results)\n\t\tif queryStats.RowCount.Int64 > int64(l.config.ShowResults) {\n\t\t\tbuf.WriteString(\"\\n...\\n(Fetched \" + strconv.FormatInt(queryStats.RowCount.Int64, 10) + \" rows)\")\n\t\t}\n\t}\n\tif buf.Len() > 0 {\n\t\tl.logger.Println(buf.String())\n\t}\n}\n\n// Log wraps a DB and adds logging to it.\nfunc Log(db DB) interface {\n\tDB\n\tSqLogger\n} {\n\treturn struct {\n\t\tDB\n\t\tSqLogger\n\t}{DB: db, SqLogger: defaultLogger}\n}\n\n// VerboseLog wraps a DB and adds verbose logging to it.\nfunc VerboseLog(db DB) interface {\n\tDB\n\tSqLogger\n} {\n\treturn struct {\n\t\tDB\n\t\tSqLogger\n\t}{DB: db, SqLogger: verboseLogger}\n}\n\nvar defaultLogSettings atomic.Value\n\n// SetDefaultLogSettings sets the function to configure the default\n// LogSettings. This value is not used unless SetDefaultLogQuery is also\n// configured.\nfunc SetDefaultLogSettings(logSettings func(context.Context, *LogSettings)) {\n\tdefaultLogSettings.Store(logSettings)\n}\n\nvar defaultLogQuery atomic.Value\n\n// SetDefaultLogQuery sets the default logging function to call for all\n// queries (if a logger is not explicitly passed in).\nfunc SetDefaultLogQuery(logQuery func(context.Context, QueryStats)) {\n\tdefaultLogQuery.Store(logQuery)\n}\n\ntype sqLogStruct struct {\n\tlogSettings func(context.Context, *LogSettings)\n\tlogQuery    func(context.Context, QueryStats)\n}\n\nvar _ SqLogger = (*sqLogStruct)(nil)\n\nfunc (l *sqLogStruct) SqLogSettings(ctx context.Context, logSettings *LogSettings) {\n\tif l.logSettings == nil {\n\t\treturn\n\t}\n\tl.logSettings(ctx, logSettings)\n}\n\nfunc (l *sqLogStruct) SqLogQuery(ctx context.Context, queryStats QueryStats) {\n\tif l.logQuery == nil {\n\t\treturn\n\t}\n\tl.logQuery(ctx, queryStats)\n}\n\nconst (\n\tcolorReset  = \"\\x1b[0m\"\n\tcolorRed    = \"\\x1b[91m\"\n\tcolorGreen  = \"\\x1b[92m\"\n\tcolorYellow = \"\\x1b[93m\"\n\tcolorBlue   = \"\\x1b[94m\"\n\tcolorPurple = \"\\x1b[95m\"\n\tcolorCyan   = \"\\x1b[96m\"\n\tcolorGray   = \"\\x1b[97m\"\n\tcolorWhite  = \"\\x1b[97m\"\n)\n"
  },
  {
    "path": "logger_test.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n)\n\nfunc TestLogger(t *testing.T) {\n\ttype TT struct {\n\t\tdescription string\n\t\tctx         context.Context\n\t\tstats       QueryStats\n\t\tconfig      LoggerConfig\n\t\twantOutput  string\n\t}\n\n\tassert := func(t *testing.T, tt TT) {\n\t\tif tt.ctx == nil {\n\t\t\ttt.ctx = context.Background()\n\t\t}\n\t\tbuf := &bytes.Buffer{}\n\t\tlogger := sqLogger{\n\t\t\tlogger: log.New(buf, \"\", 0),\n\t\t\tconfig: tt.config,\n\t\t}\n\t\tlogger.SqLogQuery(tt.ctx, tt.stats)\n\t\tif diff := testutil.Diff(buf.String(), tt.wantOutput); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t}\n\n\tt.Run(\"Log VerboseLog\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar logSettings LogSettings\n\t\tLog(nil).SqLogSettings(context.Background(), &logSettings)\n\t\tdiff := testutil.Diff(logSettings, LogSettings{\n\t\t\tLogAsynchronously: false,\n\t\t\tIncludeTime:       true,\n\t\t\tIncludeCaller:     true,\n\t\t\tIncludeResults:    0,\n\t\t})\n\t\tif diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tVerboseLog(nil).SqLogSettings(context.Background(), &logSettings)\n\t\tdiff = testutil.Diff(logSettings, LogSettings{\n\t\t\tLogAsynchronously: false,\n\t\t\tIncludeTime:       true,\n\t\t\tIncludeCaller:     true,\n\t\t\tIncludeResults:    5,\n\t\t})\n\t\tif diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"no color\", func(t *testing.T) {\n\t\tvar tt TT\n\t\ttt.config.NoColor = true\n\t\ttt.stats.Query = \"SELECT 1\"\n\t\ttt.wantOutput = \"[OK] SELECT 1;\\n\"\n\t\tassert(t, tt)\n\t})\n\n\ttests := []TT{{\n\t\tdescription: \"err\",\n\t\tstats: QueryStats{\n\t\t\tQuery: \"SELECT 1\",\n\t\t\tErr:   fmt.Errorf(\"lorem ipsum\"),\n\t\t},\n\t\twantOutput: \"\\x1b[91m[FAIL]\\x1b[0m SELECT 1;\\x1b[94m err\\x1b[0m={lorem ipsum}\\n\",\n\t}, {\n\t\tdescription: \"HideArgs\",\n\t\tconfig:      LoggerConfig{HideArgs: true},\n\t\tstats: QueryStats{\n\t\t\tQuery: \"SELECT ?\", Args: []any{1},\n\t\t},\n\t\twantOutput: \"\\x1b[92m[OK]\\x1b[0m SELECT ?;\\n\",\n\t}, {\n\t\tdescription: \"RowCount\",\n\t\tstats: QueryStats{\n\t\t\tQuery:    \"SELECT 1\",\n\t\t\tRowCount: sql.NullInt64{Valid: true, Int64: 3},\n\t\t},\n\t\twantOutput: \"\\x1b[92m[OK]\\x1b[0m SELECT 1;\\x1b[94m rowCount\\x1b[0m=3\\n\",\n\t}, {\n\t\tdescription: \"RowsAffected\",\n\t\tstats: QueryStats{\n\t\t\tQuery:        \"SELECT 1\",\n\t\t\tRowsAffected: sql.NullInt64{Valid: true, Int64: 5},\n\t\t},\n\t\twantOutput: \"\\x1b[92m[OK]\\x1b[0m SELECT 1;\\x1b[94m rowsAffected\\x1b[0m=5\\n\",\n\t}, {\n\t\tdescription: \"LastInsertId\",\n\t\tstats: QueryStats{\n\t\t\tQuery:        \"SELECT 1\",\n\t\t\tLastInsertId: sql.NullInt64{Valid: true, Int64: 7},\n\t\t},\n\t\twantOutput: \"\\x1b[92m[OK]\\x1b[0m SELECT 1;\\x1b[94m lastInsertId\\x1b[0m=7\\n\",\n\t}, {\n\t\tdescription: \"Exists\",\n\t\tstats: QueryStats{\n\t\t\tQuery:  \"SELECT EXISTS (SELECT 1)\",\n\t\t\tExists: sql.NullBool{Valid: true, Bool: true},\n\t\t},\n\t\twantOutput: \"\\x1b[92m[OK]\\x1b[0m SELECT EXISTS (SELECT 1);\\x1b[94m exists\\x1b[0m=true\\n\",\n\t}, {\n\t\tdescription: \"ShowCaller\",\n\t\tconfig:      LoggerConfig{ShowCaller: true},\n\t\tstats: QueryStats{\n\t\t\tQuery:          \"SELECT 1\",\n\t\t\tCallerFile:     \"file.go\",\n\t\t\tCallerLine:     22,\n\t\t\tCallerFunction: \"someFunc\",\n\t\t},\n\t\twantOutput: \"\\x1b[92m[OK]\\x1b[0m SELECT 1;\\x1b[94m caller\\x1b[0m=file.go:22:someFunc\\n\",\n\t}, {\n\t\tdescription: \"Verbose\",\n\t\tconfig:      LoggerConfig{InterpolateVerbose: true, ShowTimeTaken: true},\n\t\tstats: QueryStats{\n\t\t\tQuery: \"SELECT ?, ?\", Args: []any{1, \"bob\"},\n\t\t\tTimeTaken: 300 * time.Millisecond,\n\t\t},\n\t\twantOutput: \"\\x1b[92m[OK]\\x1b[0m\\x1b[94m timeTaken\\x1b[0m=300ms\" +\n\t\t\t\"\\n\\x1b[95m----[ Executing query ]----\\x1b[0m\" +\n\t\t\t\"\\nSELECT ?, ?; []interface {}{1, \\\"bob\\\"}\" +\n\t\t\t\"\\n\\x1b[95m----[ with bind values ]----\\x1b[0m\" +\n\t\t\t\"\\nSELECT 1, 'bob';\\n\",\n\t}, {\n\t\tdescription: \"ShowResults\",\n\t\tconfig:      LoggerConfig{ShowResults: 1},\n\t\tstats: QueryStats{\n\t\t\tQuery:   \"SELECT 1\",\n\t\t\tResults: \"\\nlorem ipsum dolor sit amet\",\n\t\t},\n\t\twantOutput: \"\\x1b[92m[OK]\\x1b[0m SELECT 1;\" +\n\t\t\t\"\\n\\x1b[95m----[ Fetched result ]----\\x1b[0m\" +\n\t\t\t\"\\nlorem ipsum dolor sit amet\\n\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tassert(t, tt)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "misc.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n)\n\n// ValueExpression represents an SQL value that is passed in as an argument to\n// a prepared query.\ntype ValueExpression struct {\n\tvalue any\n\talias string\n}\n\nvar _ interface {\n\tField\n\tPredicate\n\tAny\n} = (*ValueExpression)(nil)\n\n// Value returns a new ValueExpression.\nfunc Value(value any) ValueExpression { return ValueExpression{value: value} }\n\n// WriteSQL implements the SQLWriter interface.\nfunc (e ValueExpression) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn WriteValue(ctx, dialect, buf, args, params, e.value)\n}\n\n// As returns a new ValueExpression with the given alias.\nfunc (e ValueExpression) As(alias string) ValueExpression {\n\te.alias = alias\n\treturn e\n}\n\n// In returns a 'expr IN (val)' Predicate.\nfunc (e ValueExpression) In(val any) Predicate { return In(e.value, val) }\n\n// Eq returns a 'expr = val' Predicate.\nfunc (e ValueExpression) Eq(val any) Predicate { return Eq(e.value, val) }\n\n// Ne returns a 'expr <> val' Predicate.\nfunc (e ValueExpression) Ne(val any) Predicate { return Ne(e.value, val) }\n\n// Lt returns a 'expr < val' Predicate.\nfunc (e ValueExpression) Lt(val any) Predicate { return Lt(e.value, val) }\n\n// Le returns a 'expr <= val' Predicate.\nfunc (e ValueExpression) Le(val any) Predicate { return Le(e.value, val) }\n\n// Gt returns a 'expr > val' Predicate.\nfunc (e ValueExpression) Gt(val any) Predicate { return Gt(e.value, val) }\n\n// Ge returns a 'expr >= val' Predicate.\nfunc (e ValueExpression) Ge(val any) Predicate { return Ge(e.value, val) }\n\n// GetAlias returns the alias of the ValueExpression.\nfunc (e ValueExpression) GetAlias() string { return e.alias }\n\n// IsField implements the Field interface.\nfunc (e ValueExpression) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (e ValueExpression) IsArray() {}\n\n// IsBinary implements the Binary interface.\nfunc (e ValueExpression) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (e ValueExpression) IsBoolean() {}\n\n// IsEnum implements the Enum interface.\nfunc (e ValueExpression) IsEnum() {}\n\n// IsJSON implements the JSON interface.\nfunc (e ValueExpression) IsJSON() {}\n\n// IsNumber implements the Number interface.\nfunc (e ValueExpression) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (e ValueExpression) IsString() {}\n\n// IsTime implements the Time interfaces.\nfunc (e ValueExpression) IsTime() {}\n\n// IsUUID implements the UUID interface.\nfunc (e ValueExpression) IsUUID() {}\n\n// LiteralValue represents an SQL value literally interpolated into the query.\n// Doing so potentially exposes the query to SQL injection so only do this for\n// values that you trust e.g. literals and constants.\ntype LiteralValue struct {\n\tvalue any\n\talias string\n}\n\nvar _ interface {\n\tField\n\tPredicate\n\tBinary\n\tBoolean\n\tNumber\n\tString\n\tTime\n} = (*LiteralValue)(nil)\n\n// Literal returns a new LiteralValue.\nfunc Literal(value any) LiteralValue { return LiteralValue{value: value} }\n\n// WriteSQL implements the SQLWriter interface.\nfunc (v LiteralValue) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\ts, err := Sprint(dialect, v.value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbuf.WriteString(s)\n\treturn nil\n}\n\n// As returns a new LiteralValue with the given alias.\nfunc (v LiteralValue) As(alias string) LiteralValue {\n\tv.alias = alias\n\treturn v\n}\n\n// In returns a 'literal IN (val)' Predicate.\nfunc (v LiteralValue) In(val any) Predicate { return In(v, val) }\n\n// Eq returns a 'literal = val' Predicate.\nfunc (v LiteralValue) Eq(val any) Predicate { return Eq(v, val) }\n\n// Ne returns a 'literal <> val' Predicate.\nfunc (v LiteralValue) Ne(val any) Predicate { return Ne(v, val) }\n\n// Lt returns a 'literal < val' Predicate.\nfunc (v LiteralValue) Lt(val any) Predicate { return Lt(v, val) }\n\n// Le returns a 'literal <= val' Predicate.\nfunc (v LiteralValue) Le(val any) Predicate { return Le(v, val) }\n\n// Gt returns a 'literal > val' Predicate.\nfunc (v LiteralValue) Gt(val any) Predicate { return Gt(v, val) }\n\n// Ge returns a 'literal >= val' Predicate.\nfunc (v LiteralValue) Ge(val any) Predicate { return Ge(v, val) }\n\n// GetAlias returns the alias of the LiteralValue.\nfunc (v LiteralValue) GetAlias() string { return v.alias }\n\n// IsField implements the Field interface.\nfunc (v LiteralValue) IsField() {}\n\n// IsBinary implements the Binary interface.\nfunc (v LiteralValue) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (v LiteralValue) IsBoolean() {}\n\n// IsNumber implements the Number interface.\nfunc (v LiteralValue) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (v LiteralValue) IsString() {}\n\n// IsTime implements the Time interfaces.\nfunc (v LiteralValue) IsTime() {}\n\n// DialectExpression represents an SQL expression that renders differently\n// depending on the dialect.\ntype DialectExpression struct {\n\tDefault any\n\tCases   DialectCases\n}\n\n// DialectCases is a slice of DialectCases.\ntype DialectCases = []DialectCase\n\n// DialectCase holds the result to be used for a given dialect in a\n// DialectExpression.\ntype DialectCase struct {\n\tDialect string\n\tResult  any\n}\n\nvar _ interface {\n\tTable\n\tField\n\tPredicate\n\tAny\n} = (*DialectExpression)(nil)\n\n// DialectValue returns a new DialectExpression. The value passed in is used as\n// the default.\nfunc DialectValue(value any) DialectExpression {\n\treturn DialectExpression{Default: value}\n}\n\n// DialectExpr returns a new DialectExpression. The expression passed in is\n// used as the default.\nfunc DialectExpr(format string, values ...any) DialectExpression {\n\treturn DialectExpression{Default: Expr(format, values...)}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (e DialectExpression) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tfor _, Case := range e.Cases {\n\t\tif dialect == Case.Dialect {\n\t\t\treturn WriteValue(ctx, dialect, buf, args, params, Case.Result)\n\t\t}\n\t}\n\treturn WriteValue(ctx, dialect, buf, args, params, e.Default)\n}\n\n// DialectValue adds a new dialect-value pair to the DialectExpression.\nfunc (e DialectExpression) DialectValue(dialect string, value any) DialectExpression {\n\te.Cases = append(e.Cases, DialectCase{Dialect: dialect, Result: value})\n\treturn e\n}\n\n// DialectExpr adds a new dialect-expression pair to the DialectExpression.\nfunc (e DialectExpression) DialectExpr(dialect string, format string, values ...any) DialectExpression {\n\te.Cases = append(e.Cases, DialectCase{Dialect: dialect, Result: Expr(format, values...)})\n\treturn e\n}\n\n// IsTable implements the Table interface.\nfunc (e DialectExpression) IsTable() {}\n\n// IsField implements the Field interface.\nfunc (e DialectExpression) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (e DialectExpression) IsArray() {}\n\n// IsBinary implements the Binary interface.\nfunc (e DialectExpression) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (e DialectExpression) IsBoolean() {}\n\n// IsEnum implements the Enum interface.\nfunc (e DialectExpression) IsEnum() {}\n\n// IsJSON implements the JSON interface.\nfunc (e DialectExpression) IsJSON() {}\n\n// IsNumber implements the Number interface.\nfunc (e DialectExpression) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (e DialectExpression) IsString() {}\n\n// IsTime implements the Time interface.\nfunc (e DialectExpression) IsTime() {}\n\n// IsUUID implements the UUID interface.\nfunc (e DialectExpression) IsUUID() {}\n\n// CaseExpression represents an SQL CASE expression.\ntype CaseExpression struct {\n\talias   string\n\tCases   PredicateCases\n\tDefault any\n}\n\n// PredicateCases is a slice of PredicateCases.\ntype PredicateCases = []PredicateCase\n\n// PredicateCase holds the result to be used for a given predicate in a\n// CaseExpression.\ntype PredicateCase struct {\n\tPredicate Predicate\n\tResult    any\n}\n\nvar _ interface {\n\tField\n\tAny\n} = (*CaseExpression)(nil)\n\n// CaseWhen returns a new CaseExpression.\nfunc CaseWhen(predicate Predicate, result any) CaseExpression {\n\treturn CaseExpression{\n\t\tCases: PredicateCases{{Predicate: predicate, Result: result}},\n\t}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (e CaseExpression) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tbuf.WriteString(\"CASE\")\n\tif len(e.Cases) == 0 {\n\t\treturn fmt.Errorf(\"CaseExpression empty\")\n\t}\n\tvar err error\n\tfor i, Case := range e.Cases {\n\t\tbuf.WriteString(\" WHEN \")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, Case.Predicate)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"CASE #%d WHEN: %w\", i+1, err)\n\t\t}\n\t\tbuf.WriteString(\" THEN \")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, Case.Result)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"CASE #%d THEN: %w\", i+1, err)\n\t\t}\n\t}\n\tif e.Default != nil {\n\t\tbuf.WriteString(\" ELSE \")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, e.Default)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"CASE ELSE: %w\", err)\n\t\t}\n\t}\n\tbuf.WriteString(\" END\")\n\treturn nil\n}\n\n// When adds a new predicate-result pair to the CaseExpression.\nfunc (e CaseExpression) When(predicate Predicate, result any) CaseExpression {\n\te.Cases = append(e.Cases, PredicateCase{Predicate: predicate, Result: result})\n\treturn e\n}\n\n// Else sets the fallback result of the CaseExpression.\nfunc (e CaseExpression) Else(fallback any) CaseExpression {\n\te.Default = fallback\n\treturn e\n}\n\n// As returns a new CaseExpression with the given alias.\nfunc (e CaseExpression) As(alias string) CaseExpression {\n\te.alias = alias\n\treturn e\n}\n\n// GetAlias returns the alias of the CaseExpression.\nfunc (e CaseExpression) GetAlias() string { return e.alias }\n\n// IsField implements the Field interface.\nfunc (e CaseExpression) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (e CaseExpression) IsArray() {}\n\n// IsBinary implements the Binary interface.\nfunc (e CaseExpression) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (e CaseExpression) IsBoolean() {}\n\n// IsEnum implements the Enum interface.\nfunc (e CaseExpression) IsEnum() {}\n\n// IsJSON implements the JSON interface.\nfunc (e CaseExpression) IsJSON() {}\n\n// IsNumber implements the Number interface.\nfunc (e CaseExpression) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (e CaseExpression) IsString() {}\n\n// IsTime implements the Time interface.\nfunc (e CaseExpression) IsTime() {}\n\n// IsUUID implements the UUID interface.\nfunc (e CaseExpression) IsUUID() {}\n\n// SimpleCaseExpression represents an SQL simple CASE expression.\ntype SimpleCaseExpression struct {\n\talias      string\n\tExpression any\n\tCases      SimpleCases\n\tDefault    any\n}\n\n// SimpleCases is a slice of SimpleCases.\ntype SimpleCases = []SimpleCase\n\n// SimpleCase holds the result to be used for a given value in a\n// SimpleCaseExpression.\ntype SimpleCase struct {\n\tValue  any\n\tResult any\n}\n\nvar _ interface {\n\tField\n\tAny\n} = (*SimpleCaseExpression)(nil)\n\n// Case returns a new SimpleCaseExpression.\nfunc Case(expression any) SimpleCaseExpression {\n\treturn SimpleCaseExpression{Expression: expression}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (e SimpleCaseExpression) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tbuf.WriteString(\"CASE \")\n\tif len(e.Cases) == 0 {\n\t\treturn fmt.Errorf(\"SimpleCaseExpression empty\")\n\t}\n\tvar err error\n\terr = WriteValue(ctx, dialect, buf, args, params, e.Expression)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"CASE: %w\", err)\n\t}\n\tfor i, Case := range e.Cases {\n\t\tbuf.WriteString(\" WHEN \")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, Case.Value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"CASE #%d WHEN: %w\", i+1, err)\n\t\t}\n\t\tbuf.WriteString(\" THEN \")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, Case.Result)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"CASE #%d THEN: %w\", i+1, err)\n\t\t}\n\t}\n\tif e.Default != nil {\n\t\tbuf.WriteString(\" ELSE \")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, e.Default)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"CASE ELSE: %w\", err)\n\t\t}\n\t}\n\tbuf.WriteString(\" END\")\n\treturn nil\n}\n\n// When adds a new value-result pair to the SimpleCaseExpression.\nfunc (e SimpleCaseExpression) When(value any, result any) SimpleCaseExpression {\n\te.Cases = append(e.Cases, SimpleCase{Value: value, Result: result})\n\treturn e\n}\n\n// Else sets the fallback result of the SimpleCaseExpression.\nfunc (e SimpleCaseExpression) Else(fallback any) SimpleCaseExpression {\n\te.Default = fallback\n\treturn e\n}\n\n// As returns a new SimpleCaseExpression with the given alias.\nfunc (e SimpleCaseExpression) As(alias string) SimpleCaseExpression {\n\te.alias = alias\n\treturn e\n}\n\n// GetAlias returns the alias of the SimpleCaseExpression.\nfunc (e SimpleCaseExpression) GetAlias() string { return e.alias }\n\n// IsField implements the Field interface.\nfunc (e SimpleCaseExpression) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (e SimpleCaseExpression) IsArray() {}\n\n// IsBinary implements the Binary interface.\nfunc (e SimpleCaseExpression) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (e SimpleCaseExpression) IsBoolean() {}\n\n// IsEnum implements the Enum interface.\nfunc (e SimpleCaseExpression) IsEnum() {}\n\n// IsJSON implements the JSON interface.\nfunc (e SimpleCaseExpression) IsJSON() {}\n\n// IsNumber implements the Number interface.\nfunc (e SimpleCaseExpression) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (e SimpleCaseExpression) IsString() {}\n\n// IsTime implements the Time interface.\nfunc (e SimpleCaseExpression) IsTime() {}\n\n// IsUUID implements the UUID interface.\nfunc (e SimpleCaseExpression) IsUUID() {}\n\n// Count represents an SQL COUNT(<field>) expression.\nfunc Count(field Field) Expression { return Expr(\"COUNT({})\", field) }\n\n// CountStar represents an SQL COUNT(*) expression.\nfunc CountStar() Expression { return Expr(\"COUNT(*)\") }\n\n// Sum represents an SQL SUM(<num>) expression.\nfunc Sum(num Number) Expression { return Expr(\"SUM({})\", num) }\n\n// Avg represents an SQL AVG(<num>) expression.\nfunc Avg(num Number) Expression { return Expr(\"AVG({})\", num) }\n\n// Min represent an SQL MIN(<field>) expression.\nfunc Min(field Field) Expression { return Expr(\"MIN({})\", field) }\n\n// Max represents an SQL MAX(<field>) expression.\nfunc Max(field Field) Expression { return Expr(\"MAX({})\", field) }\n\n// SelectValues represents a table literal comprised of SELECT statements\n// UNION-ed together e.g.\n//\n//\t(SELECT 1 AS a, 2 AS b, 3 AS c\n//\tUNION ALL\n//\tSELECT 4, 5, 6\n//\tUNION ALL\n//\tSELECT 7, 8, 9) AS tbl\ntype SelectValues struct {\n\tAlias     string\n\tColumns   []string\n\tRowValues [][]any\n}\n\nvar _ interface {\n\tQuery\n\tTable\n} = (*SelectValues)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (vs SelectValues) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\tfor i, rowvalue := range vs.RowValues {\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\" UNION ALL \")\n\t\t}\n\t\tif len(vs.Columns) > 0 && len(rowvalue) != len(vs.Columns) {\n\t\t\treturn fmt.Errorf(\"rowvalue #%d: got %d values, want %d values (%s)\", i+1, len(rowvalue), len(vs.Columns), strings.Join(vs.Columns, \", \"))\n\t\t}\n\t\tbuf.WriteString(\"SELECT \")\n\t\tfor j, value := range rowvalue {\n\t\t\tif j > 0 {\n\t\t\t\tbuf.WriteString(\", \")\n\t\t\t}\n\t\t\terr = WriteValue(ctx, dialect, buf, args, params, value)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"rowvalue #%d value #%d: %w\", i+1, j+1, err)\n\t\t\t}\n\t\t\tif i == 0 && j < len(vs.Columns) {\n\t\t\t\tbuf.WriteString(\" AS \" + QuoteIdentifier(dialect, vs.Columns[j]))\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// Field returns a new field qualified by the SelectValues' alias.\nfunc (vs SelectValues) Field(name string) AnyField {\n\treturn NewAnyField(name, TableStruct{alias: vs.Alias})\n}\n\n// SetFetchableFields implements the Query interface. It always returns false\n// as the second result.\nfunc (vs SelectValues) SetFetchableFields([]Field) (query Query, ok bool) { return vs, false }\n\n// GetDialect implements the Query interface. It always returns an empty\n// string.\nfunc (vs SelectValues) GetDialect() string { return \"\" }\n\n// GetAlias returns the alias of the SelectValues.\nfunc (vs SelectValues) GetAlias() string { return vs.Alias }\n\n// IsTable implements the Table interface.\nfunc (vs SelectValues) IsTable() {}\n\n// TableValues represents a table literal created by the VALUES clause e.g.\n//\n// (VALUES\n//\n//\t(1, 2, 3),\n//\t(4, 5, 6),\n//\t(7, 8, 9)) AS tbl (a, b, c)\ntype TableValues struct {\n\tAlias     string\n\tColumns   []string\n\tRowValues [][]any\n}\n\nvar _ interface {\n\tQuery\n\tTable\n} = (*TableValues)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (vs TableValues) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tif len(vs.RowValues) == 0 {\n\t\treturn nil\n\t}\n\tvar err error\n\tbuf.WriteString(\"VALUES \")\n\tfor i, rowvalue := range vs.RowValues {\n\t\tif len(vs.Columns) > 0 && len(vs.Columns) != len(rowvalue) {\n\t\t\treturn fmt.Errorf(\"rowvalue #%d: got %d values, want %d values (%s)\", i+1, len(rowvalue), len(vs.Columns), strings.Join(vs.Columns, \", \"))\n\t\t}\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\tif dialect == DialectMySQL {\n\t\t\tbuf.WriteString(\"ROW(\")\n\t\t} else {\n\t\t\tbuf.WriteString(\"(\")\n\t\t}\n\t\tfor j, value := range rowvalue {\n\t\t\tif j > 0 {\n\t\t\t\tbuf.WriteString(\", \")\n\t\t\t}\n\t\t\terr = WriteValue(ctx, dialect, buf, args, params, value)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"rowvalue #%d value #%d: %w\", i+1, j+1, err)\n\t\t\t}\n\t\t}\n\t\tbuf.WriteString(\")\")\n\t}\n\treturn nil\n}\n\n// Field returns a new field qualified by the TableValues' alias.\nfunc (vs TableValues) Field(name string) AnyField {\n\treturn NewAnyField(name, TableStruct{alias: vs.Alias})\n}\n\n// SetFetchableFields implements the Query interface. It always returns false\n// as the second result.\nfunc (vs TableValues) SetFetchableFields([]Field) (query Query, ok bool) { return vs, false }\n\n// GetDialect implements the Query interface. It always returns an empty\n// string.\nfunc (vs TableValues) GetDialect() string { return \"\" }\n\n// GetAlias returns the alias of the TableValues.\nfunc (vs TableValues) GetAlias() string { return vs.Alias }\n\n// GetColumns returns the names of the columns in the TableValues.\nfunc (vs TableValues) GetColumns() []string { return vs.Columns }\n\n// IsTable implements the Table interface.\nfunc (vs TableValues) IsTable() {}\n"
  },
  {
    "path": "misc_test.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n)\n\nfunc TestValueExpression(t *testing.T) {\n\tt.Run(\"alias\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\texpr := Value(1).As(\"num\")\n\t\tif diff := testutil.Diff(expr.GetAlias(), \"num\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\ttests := []TestTable{{\n\t\tdescription: \"basic\",\n\t\titem:        Value(Param(\"xyz\", 42)),\n\t\twantQuery:   \"?\",\n\t\twantArgs:    []any{42},\n\t\twantParams:  map[string][]int{\"xyz\": {0}},\n\t}, {\n\t\tdescription: \"In\", item: Value(1).In([]int{18, 21, 32}),\n\t\twantQuery: \"? IN (?, ?, ?)\", wantArgs: []any{1, 18, 21, 32},\n\t}, {\n\t\tdescription: \"Eq\", item: Value(1).Eq(34),\n\t\twantQuery: \"? = ?\", wantArgs: []any{1, 34},\n\t}, {\n\t\tdescription: \"Ne\", item: Value(1).Ne(34),\n\t\twantQuery: \"? <> ?\", wantArgs: []any{1, 34},\n\t}, {\n\t\tdescription: \"Lt\", item: Value(1).Lt(34),\n\t\twantQuery: \"? < ?\", wantArgs: []any{1, 34},\n\t}, {\n\t\tdescription: \"Le\", item: Value(1).Le(34),\n\t\twantQuery: \"? <= ?\", wantArgs: []any{1, 34},\n\t}, {\n\t\tdescription: \"Gt\", item: Value(1).Gt(34),\n\t\twantQuery: \"? > ?\", wantArgs: []any{1, 34},\n\t}, {\n\t\tdescription: \"Ge\", item: Value(1).Ge(34),\n\t\twantQuery: \"? >= ?\", wantArgs: []any{1, 34},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\nfunc TestLiteralExpression(t *testing.T) {\n\tt.Run(\"alias\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\texpr := Literal(1).As(\"num\")\n\t\tif diff := testutil.Diff(expr.GetAlias(), \"num\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\ttests := []TestTable{{\n\t\tdescription: \"binary\",\n\t\titem:        Literal([]byte{0xab, 0xcd, 0xef}),\n\t\twantQuery:   \"x'abcdef'\",\n\t}, {\n\t\tdescription: \"time\", item: Literal(time.Unix(0, 0).UTC()),\n\t\twantQuery: \"'1970-01-01 00:00:00'\",\n\t}, {\n\t\tdescription: \"In\", item: Literal(1).In([]any{Literal(18), Literal(21), Literal(32)}),\n\t\twantQuery: \"1 IN (18, 21, 32)\",\n\t}, {\n\t\tdescription: \"Eq\", item: Literal(true).Eq(Literal(false)),\n\t\twantQuery: \"TRUE = FALSE\",\n\t}, {\n\t\tdescription: \"Ne\", item: Literal(\"one\").Ne(Literal(\"thirty four\")),\n\t\twantQuery: \"'one' <> 'thirty four'\",\n\t}, {\n\t\tdescription: \"Lt\", item: Literal(1).Lt(Literal(34)),\n\t\twantQuery: \"1 < 34\",\n\t}, {\n\t\tdescription: \"Le\", item: Literal(1).Le(Literal(34)),\n\t\twantQuery: \"1 <= 34\",\n\t}, {\n\t\tdescription: \"Gt\", item: Literal(1).Gt(Literal(34)),\n\t\twantQuery: \"1 > 34\",\n\t}, {\n\t\tdescription: \"Ge\", item: Literal(1).Ge(Literal(34)),\n\t\twantQuery: \"1 >= 34\",\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n\nfunc TestDialectExpression(t *testing.T) {\n\tt.Parallel()\n\texpr := DialectValue(Expr(\"default\")).\n\t\tDialectValue(DialectSQLite, Expr(\"sqlite\")).\n\t\tDialectValue(DialectPostgres, Expr(\"postgres\")).\n\t\tDialectValue(DialectMySQL, Expr(\"mysql\")).\n\t\tDialectExpr(DialectSQLServer, \"{}\", Expr(\"sqlserver\"))\n\tvar tt TestTable\n\ttt.item = expr\n\t// default\n\ttt.wantQuery = \"default\"\n\ttt.assert(t)\n\t// sqlite\n\ttt.dialect = DialectSQLite\n\ttt.wantQuery = \"sqlite\"\n\ttt.assert(t)\n\t// postgres\n\ttt.dialect = DialectPostgres\n\ttt.wantQuery = \"postgres\"\n\ttt.assert(t)\n\t// mysql\n\ttt.dialect = DialectMySQL\n\ttt.wantQuery = \"mysql\"\n\ttt.assert(t)\n\t// sqlserver\n\ttt.dialect = DialectSQLServer\n\ttt.wantQuery = \"sqlserver\"\n\ttt.assert(t)\n}\n\nfunc TestCaseExpressions(t *testing.T) {\n\tt.Run(\"name and alias\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t// CaseExpression\n\t\tcaseExpr := CaseWhen(Value(true), 1).As(\"result_a\")\n\t\tif diff := testutil.Diff(caseExpr.GetAlias(), \"result_a\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t// SimpleCaseExpression\n\t\tsimpleCaseExpr := Case(1).When(1, 2).As(\"result_b\")\n\t\tif diff := testutil.Diff(simpleCaseExpr.GetAlias(), \"result_b\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tt.Run(\"CaseExpression\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tTestTable{\n\t\t\titem:      CaseWhen(Expr(\"x = y\"), 1).When(Expr(\"a = b\"), 2).Else(3),\n\t\t\twantQuery: \"CASE WHEN x = y THEN ? WHEN a = b THEN ? ELSE ? END\",\n\t\t\twantArgs:  []any{1, 2, 3},\n\t\t}.assert(t)\n\t})\n\n\tt.Run(\"SimpleCaseExpression\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tTestTable{\n\t\t\titem:      Case(Expr(\"a\")).When(1, 2).When(3, 4).Else(5),\n\t\t\twantQuery: \"CASE a WHEN ? THEN ? WHEN ? THEN ? ELSE ? END\",\n\t\t\twantArgs:  []any{1, 2, 3, 4, 5},\n\t\t}.assert(t)\n\t})\n\n\tt.Run(\"CaseExpression cannot be empty\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tTestTable{item: CaseExpression{}}.assertNotOK(t)\n\t})\n\n\tt.Run(\"SimpleCaseExpression cannot be empty\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tTestTable{item: SimpleCaseExpression{}}.assertNotOK(t)\n\t})\n\n\terrTests := []TestTable{{\n\t\tdescription: \"CASE WHEN predicate err\", item: CaseWhen(FaultySQL{}, 1),\n\t}, {\n\t\tdescription: \"CASE WHEN result err\", item: CaseWhen(Value(true), FaultySQL{}),\n\t}, {\n\t\tdescription: \"CASE WHEN fallback err\", item: CaseWhen(Value(true), 1).Else(FaultySQL{}),\n\t}, {\n\t\tdescription: \"CASE expression err\", item: Case(FaultySQL{}).When(1, 2),\n\t}, {\n\t\tdescription: \"CASE value err\", item: Case(1).When(FaultySQL{}, 2),\n\t}, {\n\t\tdescription: \"CASE result err\", item: Case(1).When(2, FaultySQL{}),\n\t}, {\n\t\tdescription: \"CASE fallback err\", item: Case(1).When(2, 3).Else(FaultySQL{}),\n\t}}\n\n\tfor _, tt := range errTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertErr(t, ErrFaultySQL)\n\t\t})\n\t}\n}\n\nfunc TestSelectValues(t *testing.T) {\n\ttype TestTable struct {\n\t\tdescription string\n\t\tdialect     string\n\t\titem        SelectValues\n\t\twantQuery   string\n\t\twantArgs    []any\n\t}\n\n\tt.Run(\"dialect alias and fields\", func(t *testing.T) {\n\t\tselectValues := SelectValues{\n\t\t\tAlias: \"aaa\",\n\t\t}\n\t\tif diff := testutil.Diff(selectValues.GetAlias(), \"aaa\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif diff := testutil.Diff(selectValues.GetDialect(), \"\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := selectValues.SetFetchableFields(nil)\n\t\tif diff := testutil.Diff(ok, false); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tgotField, _, _ := ToSQL(\"\", selectValues.Field(\"bbb\"), nil)\n\t\tif diff := testutil.Diff(gotField, \"aaa.bbb\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\ttests := []TestTable{{\n\t\tdescription: \"empty\",\n\t\titem:        SelectValues{},\n\t\twantQuery:   \"\",\n\t\twantArgs:    nil,\n\t}, {\n\t\tdescription: \"no columns\",\n\t\titem: SelectValues{\n\t\t\tRowValues: [][]any{\n\t\t\t\t{1, 2, 3},\n\t\t\t\t{4, 5, 6},\n\t\t\t\t{7, 8, 9},\n\t\t\t},\n\t\t},\n\t\twantQuery: \"SELECT ?, ?, ?\" +\n\t\t\t\" UNION ALL SELECT ?, ?, ?\" +\n\t\t\t\" UNION ALL SELECT ?, ?, ?\",\n\t\twantArgs: []any{1, 2, 3, 4, 5, 6, 7, 8, 9},\n\t}, {\n\t\tdescription: \"postgres\",\n\t\tdialect:     DialectPostgres,\n\t\titem: SelectValues{\n\t\t\tColumns: []string{\"a\", \"b\", \"c\"},\n\t\t\tRowValues: [][]any{\n\t\t\t\t{1, 2, 3},\n\t\t\t\t{4, 5, 6},\n\t\t\t\t{7, 8, 9},\n\t\t\t},\n\t\t},\n\t\twantQuery: \"SELECT $1 AS a, $2 AS b, $3 AS c\" +\n\t\t\t\" UNION ALL SELECT $4, $5, $6\" +\n\t\t\t\" UNION ALL SELECT $7, $8, $9\",\n\t\twantArgs: []any{1, 2, 3, 4, 5, 6, 7, 8, 9},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tvar buf bytes.Buffer\n\t\t\tvar gotArgs []any\n\t\t\terr := tt.item.WriteSQL(context.Background(), tt.dialect, &buf, &gotArgs, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\tgotQuery := buf.String()\n\t\t\tif diff := testutil.Diff(gotQuery, tt.wantQuery); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(gotArgs, tt.wantArgs); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTableValues(t *testing.T) {\n\ttype TestTable struct {\n\t\tdescription string\n\t\tdialect     string\n\t\titem        TableValues\n\t\twantQuery   string\n\t\twantArgs    []any\n\t}\n\n\tt.Run(\"dialect alias columns and fields\", func(t *testing.T) {\n\t\ttableValues := TableValues{\n\t\t\tAlias:   \"aaa\",\n\t\t\tColumns: []string{\"a\", \"b\", \"c\"},\n\t\t}\n\t\tif diff := testutil.Diff(tableValues.GetAlias(), \"aaa\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif diff := testutil.Diff(tableValues.GetDialect(), \"\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := tableValues.SetFetchableFields(nil)\n\t\tif diff := testutil.Diff(ok, false); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tgotColumns := tableValues.GetColumns()\n\t\twantColumns := []string{\"a\", \"b\", \"c\"}\n\t\tif diff := testutil.Diff(gotColumns, wantColumns); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tgotField, _, _ := ToSQL(\"\", tableValues.Field(\"bbb\"), nil)\n\t\twantField := \"aaa.bbb\"\n\t\tif diff := testutil.Diff(gotField, wantField); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\ttests := []TestTable{{\n\t\tdescription: \"empty\",\n\t\titem:        TableValues{},\n\t\twantQuery:   \"\",\n\t\twantArgs:    nil,\n\t}, {\n\t\tdescription: \"no columns\",\n\t\titem: TableValues{\n\t\t\tRowValues: [][]any{\n\t\t\t\t{1, 2, 3},\n\t\t\t\t{4, 5, 6},\n\t\t\t\t{7, 8, 9},\n\t\t\t},\n\t\t},\n\t\twantQuery: \"VALUES (?, ?, ?)\" +\n\t\t\t\", (?, ?, ?)\" +\n\t\t\t\", (?, ?, ?)\",\n\t\twantArgs: []any{1, 2, 3, 4, 5, 6, 7, 8, 9},\n\t}, {\n\t\tdescription: \"postgres\",\n\t\tdialect:     DialectPostgres,\n\t\titem: TableValues{\n\t\t\tColumns: []string{\"a\", \"b\", \"c\"},\n\t\t\tRowValues: [][]any{\n\t\t\t\t{1, 2, 3},\n\t\t\t\t{4, 5, 6},\n\t\t\t\t{7, 8, 9},\n\t\t\t},\n\t\t},\n\t\twantQuery: \"VALUES ($1, $2, $3)\" +\n\t\t\t\", ($4, $5, $6)\" +\n\t\t\t\", ($7, $8, $9)\",\n\t\twantArgs: []any{1, 2, 3, 4, 5, 6, 7, 8, 9},\n\t}, {\n\t\tdescription: \"mysql\",\n\t\tdialect:     DialectMySQL,\n\t\titem: TableValues{\n\t\t\tColumns: []string{\"a\", \"b\", \"c\"},\n\t\t\tRowValues: [][]any{\n\t\t\t\t{1, 2, 3},\n\t\t\t\t{4, 5, 6},\n\t\t\t\t{7, 8, 9},\n\t\t\t},\n\t\t},\n\t\twantQuery: \"VALUES ROW(?, ?, ?)\" +\n\t\t\t\", ROW(?, ?, ?)\" +\n\t\t\t\", ROW(?, ?, ?)\",\n\t\twantArgs: []any{1, 2, 3, 4, 5, 6, 7, 8, 9},\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tvar buf bytes.Buffer\n\t\t\tvar gotArgs []any\n\t\t\terr := tt.item.WriteSQL(context.Background(), tt.dialect, &buf, &gotArgs, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\tgotQuery := buf.String()\n\t\t\tif diff := testutil.Diff(gotQuery, tt.wantQuery); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(gotArgs, tt.wantArgs); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "row_column.go",
    "content": "package sq\n\nimport (\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/bokwoon95/sq/internal/googleuuid\"\n\t\"github.com/bokwoon95/sq/internal/pqarray\"\n)\n\n// Row represents the state of a row after a call to rows.Next().\ntype Row struct {\n\tdialect       string\n\tsqlRows       *sql.Rows\n\trunningIndex  int\n\tfields        []Field\n\tscanDest      []any\n\tqueryIsStatic bool\n\tcolumns       []string\n\tcolumnTypes   []*sql.ColumnType\n\tvalues        []any\n\tcolumnIndex   map[string]int\n}\n\n// Column returns the names of the columns returned by the query. This method\n// can only be called in a rowmapper if it is paired with a raw SQL query e.g.\n// Queryf(\"SELECT * FROM my_table\"). Otherwise, an error will be returned.\nfunc (row *Row) Columns() []string {\n\tif row.queryIsStatic {\n\t\treturn row.columns\n\t}\n\tif row.sqlRows == nil {\n\t\treturn nil\n\t}\n\tcolumns, err := row.sqlRows.Columns()\n\tif err != nil {\n\t\tpanic(fmt.Errorf(callsite(1)+\"sqlRows.Columns: %w\", err))\n\t}\n\treturn columns\n}\n\n// ColumnTypes returns the column types returned by the query. This method can\n// only be called in a rowmapper if it is paired with a raw SQL query e.g.\n// Queryf(\"SELECT * FROM my_table\"). Otherwise, an error will be returned.\nfunc (row *Row) ColumnTypes() []*sql.ColumnType {\n\tif row.queryIsStatic {\n\t\treturn row.columnTypes\n\t}\n\tif row.sqlRows == nil {\n\t\treturn nil\n\t}\n\tcolumnTypes, err := row.sqlRows.ColumnTypes()\n\tif err != nil {\n\t\tpanic(fmt.Errorf(callsite(1)+\"sqlRows.ColumnTypes: %w\", err))\n\t}\n\treturn columnTypes\n}\n\n// Values returns the values of the current row. This method can only be called\n// in a rowmapper if it is paired with a raw SQL query e.g. Queryf(\"SELECT *\n// FROM my_table\"). Otherwise, an error will be returned.\nfunc (row *Row) Values() []any {\n\tif row.queryIsStatic {\n\t\tvalues := make([]any, len(row.values))\n\t\tcopy(values, row.values)\n\t\treturn values\n\t}\n\tif row.sqlRows == nil {\n\t\treturn nil\n\t}\n\tcolumns, err := row.sqlRows.Columns()\n\tif err != nil {\n\t\tpanic(fmt.Errorf(callsite(1)+\"sqlRows.Columns: %w\", err))\n\t}\n\tvalues := make([]any, len(columns))\n\tscanDest := make([]any, len(columns))\n\tfor i := range values {\n\t\tscanDest[i] = &values[i]\n\t}\n\terr = row.sqlRows.Scan(scanDest...)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(callsite(1)+\"sqlRows.Scan: %w\", err))\n\t}\n\treturn values\n}\n\n// Value returns the value of the expression. It is intended for use cases\n// where you only know the name of the column but not its type to scan into.\n// The underlying type of the value is determined by the database driver you\n// are using.\nfunc (row *Row) Value(format string, values ...any) any {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s is not present in query (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\treturn row.values[index]\n\t}\n\tif row.sqlRows == nil {\n\t\tvar value any\n\t\trow.fields = append(row.fields, Expr(format, values...))\n\t\trow.scanDest = append(row.scanDest, &value)\n\t\treturn nil\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*any)\n\treturn *scanDest\n}\n\n// Scan scans the expression into destPtr.\nfunc (row *Row) Scan(destPtr any, format string, values ...any) {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call Scan for static queries\"))\n\t}\n\trow.scan(destPtr, Expr(format, values...), 1)\n}\n\n// ScanField scans the field into destPtr.\nfunc (row *Row) ScanField(destPtr any, field Field) {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call ScanField for static queries\"))\n\t}\n\trow.scan(destPtr, field, 1)\n}\n\nfunc (row *Row) scan(destPtr any, field Field, skip int) {\n\tif row.sqlRows == nil {\n\t\trow.fields = append(row.fields, field)\n\t\tswitch destPtr.(type) {\n\t\tcase *bool, *sql.NullBool:\n\t\t\trow.scanDest = append(row.scanDest, &sql.NullBool{})\n\t\tcase *float64, *sql.NullFloat64:\n\t\t\trow.scanDest = append(row.scanDest, &sql.NullFloat64{})\n\t\tcase *int32, *sql.NullInt32:\n\t\t\trow.scanDest = append(row.scanDest, &sql.NullInt32{})\n\t\tcase *int, *int64, *sql.NullInt64:\n\t\t\trow.scanDest = append(row.scanDest, &sql.NullInt64{})\n\t\tcase *string, *sql.NullString:\n\t\t\trow.scanDest = append(row.scanDest, &sql.NullString{})\n\t\tcase *time.Time, *sql.NullTime:\n\t\t\trow.scanDest = append(row.scanDest, &sql.NullTime{})\n\t\tdefault:\n\t\t\tif reflect.TypeOf(destPtr).Kind() != reflect.Ptr {\n\t\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"cannot pass in non pointer value (%#v) as destPtr\", destPtr))\n\t\t\t}\n\t\t\trow.scanDest = append(row.scanDest, destPtr)\n\t\t}\n\t\treturn\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tswitch destPtr := destPtr.(type) {\n\tcase *bool:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullBool)\n\t\t*destPtr = scanDest.Bool\n\tcase *sql.NullBool:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullBool)\n\t\t*destPtr = *scanDest\n\tcase *float64:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullFloat64)\n\t\t*destPtr = scanDest.Float64\n\tcase *sql.NullFloat64:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullFloat64)\n\t\t*destPtr = *scanDest\n\tcase *int:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullInt64)\n\t\t*destPtr = int(scanDest.Int64)\n\tcase *int32:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullInt32)\n\t\t*destPtr = scanDest.Int32\n\tcase *sql.NullInt32:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullInt32)\n\t\t*destPtr = *scanDest\n\tcase *int64:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullInt64)\n\t\t*destPtr = scanDest.Int64\n\tcase *sql.NullInt64:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullInt64)\n\t\t*destPtr = *scanDest\n\tcase *string:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullString)\n\t\t*destPtr = scanDest.String\n\tcase *sql.NullString:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullString)\n\t\t*destPtr = *scanDest\n\tcase *time.Time:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullTime)\n\t\t*destPtr = scanDest.Time\n\tcase *sql.NullTime:\n\t\tscanDest := row.scanDest[row.runningIndex].(*sql.NullTime)\n\t\t*destPtr = *scanDest\n\tdefault:\n\t\tdestValue := reflect.ValueOf(destPtr).Elem()\n\t\tsrcValue := reflect.ValueOf(row.scanDest[row.runningIndex]).Elem()\n\t\tdestValue.Set(srcValue)\n\t}\n}\n\n// Array scans the array expression into destPtr. The destPtr must be a pointer\n// to a []string, []int, []int64, []int32, []float64, []float32 or []bool.\nfunc (row *Row) Array(destPtr any, format string, values ...any) {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call Array for static queries\"))\n\t}\n\trow.array(destPtr, Expr(format, values...), 1)\n}\n\n// ArrayField scans the array field into destPtr. The destPtr must be a pointer\n// to a []string, []int, []int64, []int32, []float64, []float32 or []bool.\nfunc (row *Row) ArrayField(destPtr any, field Array) {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call ArrayField for static queries\"))\n\t}\n\trow.array(destPtr, field, 1)\n}\n\nfunc (row *Row) array(destPtr any, field Array, skip int) {\n\tif row.sqlRows == nil {\n\t\tif reflect.TypeOf(destPtr).Kind() != reflect.Ptr {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"cannot pass in non pointer value (%#v) as destPtr\", destPtr))\n\t\t}\n\t\tif row.dialect == DialectPostgres {\n\t\t\tswitch destPtr.(type) {\n\t\t\tcase *[]string, *[]int, *[]int64, *[]int32, *[]float64, *[]float32, *[]bool:\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"destptr (%T) must be either a pointer to a []string, []int, []int64, []int32, []float64, []float32 or []bool\", destPtr))\n\t\t\t}\n\t\t}\n\t\trow.fields = append(row.fields, field)\n\t\trow.scanDest = append(row.scanDest, &nullBytes{\n\t\t\tdialect:     row.dialect,\n\t\t\tdisplayType: displayTypeString,\n\t\t})\n\t\treturn\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*nullBytes)\n\tif !scanDest.valid {\n\t\treturn\n\t}\n\tif row.dialect != DialectPostgres {\n\t\terr := json.Unmarshal(scanDest.bytes, destPtr)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"unmarshaling json %q into %T: %w\", string(scanDest.bytes), destPtr, err))\n\t\t}\n\t\treturn\n\t}\n\tswitch destPtr := destPtr.(type) {\n\tcase *[]string:\n\t\tvar array pqarray.StringArray\n\t\terr := array.Scan(scanDest.bytes)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"unable to convert %q to string array: %w\", string(scanDest.bytes), err))\n\t\t}\n\t\t*destPtr = array\n\tcase *[]int:\n\t\tvar array pqarray.Int64Array\n\t\terr := array.Scan(scanDest.bytes)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"unable to convert %q to int64 array: %w\", string(scanDest.bytes), err))\n\t\t}\n\t\t*destPtr = (*destPtr)[:cap(*destPtr)]\n\t\tif len(*destPtr) < len(array) {\n\t\t\t*destPtr = make([]int, len(array))\n\t\t}\n\t\t*destPtr = (*destPtr)[:len(array)]\n\t\tfor i, num := range array {\n\t\t\t(*destPtr)[i] = int(num)\n\t\t}\n\tcase *[]int64:\n\t\tvar array pqarray.Int64Array\n\t\terr := array.Scan(scanDest.bytes)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"unable to convert %q to int64 array: %w\", string(scanDest.bytes), err))\n\t\t}\n\t\t*destPtr = array\n\tcase *[]int32:\n\t\tvar array pqarray.Int32Array\n\t\terr := array.Scan(scanDest.bytes)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"unable to convert %q to int32 array: %w\", string(scanDest.bytes), err))\n\t\t}\n\t\t*destPtr = array\n\tcase *[]float64:\n\t\tvar array pqarray.Float64Array\n\t\terr := array.Scan(scanDest.bytes)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"unable to convert %q to float64 array: %w\", string(scanDest.bytes), err))\n\t\t}\n\t\t*destPtr = array\n\tcase *[]float32:\n\t\tvar array pqarray.Float32Array\n\t\terr := array.Scan(scanDest.bytes)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"unable to convert %q to float32 array: %w\", string(scanDest.bytes), err))\n\t\t}\n\t\t*destPtr = array\n\tcase *[]bool:\n\t\tvar array pqarray.BoolArray\n\t\terr := array.Scan(scanDest.bytes)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"unable to convert %q to bool array: %w\", string(scanDest.bytes), err))\n\t\t}\n\t\t*destPtr = array\n\tdefault:\n\t\tpanic(fmt.Errorf(callsite(skip+1)+\"destptr (%T) must be either a pointer to a []string, []int, []int64, []int32, []float64, []float32 or []bool\", destPtr))\n\t}\n}\n\n// Bytes returns the []byte value of the expression.\nfunc (row *Row) Bytes(format string, values ...any) []byte {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is int64, not []byte\", value))\n\t\tcase float64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is float64, not []byte\", value))\n\t\tcase bool:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is bool, not []byte\", value))\n\t\tcase []byte:\n\t\t\treturn value\n\t\tcase string:\n\t\t\treturn []byte(value)\n\t\tcase time.Time:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is time.Time, not []byte\", value))\n\t\tcase nil:\n\t\t\treturn nil\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not []byte\", value))\n\t\t}\n\t}\n\tif row.sqlRows == nil {\n\t\trow.fields = append(row.fields, Expr(format, values...))\n\t\trow.scanDest = append(row.scanDest, &nullBytes{\n\t\t\tdialect: row.dialect,\n\t\t})\n\t\treturn nil\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*nullBytes)\n\tvar b []byte\n\tif scanDest.valid {\n\t\tb = make([]byte, len(scanDest.bytes))\n\t\tcopy(b, scanDest.bytes)\n\t}\n\treturn b\n}\n\n// BytesField returns the []byte value of the field.\nfunc (row *Row) BytesField(field Binary) []byte {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call BytesField for static queries\"))\n\t}\n\tif row.sqlRows == nil {\n\t\trow.fields = append(row.fields, field)\n\t\trow.scanDest = append(row.scanDest, &nullBytes{\n\t\t\tdialect: row.dialect,\n\t\t})\n\t\treturn nil\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*nullBytes)\n\tvar b []byte\n\tif scanDest.valid {\n\t\tb = make([]byte, len(scanDest.bytes))\n\t\tcopy(b, scanDest.bytes)\n\t}\n\treturn b\n}\n\n// == Bool == //\n\n// Bool returns the bool value of the expression.\nfunc (row *Row) Bool(format string, values ...any) bool {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\tif value == 1 {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif value == 0 {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is int64, not bool\", value))\n\t\tcase float64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is float64, not bool\", value))\n\t\tcase bool:\n\t\t\treturn value\n\t\tcase []byte:\n\t\t\t// Special case: go-mysql-driver returns everything as []byte.\n\t\t\tif string(value) == \"1\" {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif string(value) == \"0\" {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%#v is []byte, not bool\", value))\n\t\tcase string:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%q is string, not bool\", value))\n\t\tcase time.Time:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is time.Time, not bool\", value))\n\t\tcase nil:\n\t\t\treturn false\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not bool\", value))\n\t\t}\n\t}\n\treturn row.NullBoolField(Expr(format, values...)).Bool\n}\n\n// BoolField returns the bool value of the field.\nfunc (row *Row) BoolField(field Boolean) bool {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call BoolField for static queries\"))\n\t}\n\treturn row.NullBoolField(field).Bool\n}\n\n// NullBool returns the sql.NullBool value of the expression.\nfunc (row *Row) NullBool(format string, values ...any) sql.NullBool {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\tif value == 1 {\n\t\t\t\treturn sql.NullBool{Bool: true, Valid: true}\n\t\t\t}\n\t\t\tif value == 0 {\n\t\t\t\treturn sql.NullBool{Bool: false, Valid: true}\n\t\t\t}\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is int64, not bool\", value))\n\t\tcase float64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is float64, not bool\", value))\n\t\tcase bool:\n\t\t\treturn sql.NullBool{Bool: value, Valid: true}\n\t\tcase []byte:\n\t\t\t// Special case: go-mysql-driver returns everything as []byte.\n\t\t\tif string(value) == \"1\" {\n\t\t\t\treturn sql.NullBool{Bool: true, Valid: true}\n\t\t\t}\n\t\t\tif string(value) == \"0\" {\n\t\t\t\treturn sql.NullBool{Bool: false, Valid: true}\n\t\t\t}\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is []byte, not bool\", value))\n\t\tcase string:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%q is string, not bool\", value))\n\t\tcase time.Time:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is time.Time, not bool\", value))\n\t\tcase nil:\n\t\t\treturn sql.NullBool{}\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not bool\", value))\n\t\t}\n\t}\n\treturn row.NullBoolField(Expr(format, values...))\n}\n\n// NullBoolField returns the sql.NullBool value of the field.\nfunc (row *Row) NullBoolField(field Boolean) sql.NullBool {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call NullBoolField for static queries\"))\n\t}\n\tif row.sqlRows == nil {\n\t\trow.fields = append(row.fields, field)\n\t\trow.scanDest = append(row.scanDest, &sql.NullBool{})\n\t\treturn sql.NullBool{}\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*sql.NullBool)\n\treturn *scanDest\n}\n\n// Enum scans the enum expression into destPtr.\nfunc (row *Row) Enum(destPtr Enumeration, format string, values ...any) {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call Enum for static queries\"))\n\t}\n\trow.enum(destPtr, Expr(format, values...), 1)\n}\n\n// EnumField scans the enum field into destPtr.\nfunc (row *Row) EnumField(destPtr Enumeration, field Enum) {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call EnumField for static queries\"))\n\t}\n\trow.enum(destPtr, field, 1)\n}\n\nfunc (row *Row) enum(destPtr Enumeration, field Enum, skip int) {\n\tif row.sqlRows == nil {\n\t\tdestType := reflect.TypeOf(destPtr)\n\t\tif destType.Kind() != reflect.Ptr {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"cannot pass in non pointer value (%#v) as destPtr\", destPtr))\n\t\t}\n\t\trow.fields = append(row.fields, field)\n\t\tswitch destType.Elem().Kind() {\n\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,\n\t\t\treflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,\n\t\t\treflect.String:\n\t\t\trow.scanDest = append(row.scanDest, &sql.NullString{})\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"underlying type of %[1]v is neither an integer or string (%[1]T)\", destPtr))\n\t\t}\n\t\treturn\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*sql.NullString)\n\tnames := destPtr.Enumerate()\n\tenumIndex := 0\n\tdestValue := reflect.ValueOf(destPtr).Elem()\n\tif scanDest.Valid {\n\t\tenumIndex = getEnumIndex(scanDest.String, names, destValue.Type())\n\t}\n\tif enumIndex < 0 {\n\t\tpanic(fmt.Errorf(callsite(skip+1)+\"%q is not a valid %T\", scanDest.String, destPtr))\n\t}\n\tswitch destValue.Kind() {\n\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\tdestValue.SetInt(int64(enumIndex))\n\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\tdestValue.SetUint(uint64(enumIndex))\n\tcase reflect.String:\n\t\tdestValue.SetString(scanDest.String)\n\t}\n}\n\n// Float64 returns the float64 value of the expression.\nfunc (row *Row) Float64(format string, values ...any) float64 {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\treturn float64(value)\n\t\tcase float64:\n\t\t\treturn value\n\t\tcase bool:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is bool, not float64\", value))\n\t\tcase []byte:\n\t\t\t// Special case: go-mysql-driver returns everything as []byte.\n\t\t\tn, err := strconv.ParseFloat(string(value), 64)\n\t\t\tif err != nil {\n\t\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is []byte, not float64\", value))\n\t\t\t}\n\t\t\treturn n\n\t\tcase string:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%q is string, not float64\", value))\n\t\tcase time.Time:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is time.Time, not float64\", value))\n\t\tcase nil:\n\t\t\treturn 0\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not float64\", value))\n\t\t}\n\t}\n\treturn row.NullFloat64Field(Expr(format, values...)).Float64\n}\n\n// Float64Field returns the float64 value of the field.\nfunc (row *Row) Float64Field(field Number) float64 {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call Float64Field for static queries\"))\n\t}\n\treturn row.NullFloat64Field(field).Float64\n}\n\n// NullFloat64 returns the sql.NullFloat64 valye of the expression.\nfunc (row *Row) NullFloat64(format string, values ...any) sql.NullFloat64 {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\treturn sql.NullFloat64{Float64: float64(value), Valid: true}\n\t\tcase float64:\n\t\t\treturn sql.NullFloat64{Float64: value, Valid: true}\n\t\tcase bool:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is bool, not float64\", value))\n\t\tcase []byte:\n\t\t\t// Special case: go-mysql-driver returns everything as []byte.\n\t\t\tn, err := strconv.ParseFloat(string(value), 64)\n\t\t\tif err != nil {\n\t\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is []byte, not float64\", value))\n\t\t\t}\n\t\t\treturn sql.NullFloat64{Float64: n, Valid: true}\n\t\tcase string:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%q is string, not float64\", value))\n\t\tcase time.Time:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is time.Time, not float64\", value))\n\t\tcase nil:\n\t\t\treturn sql.NullFloat64{}\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not float64\", value))\n\t\t}\n\t}\n\treturn row.NullFloat64Field(Expr(format, values...))\n}\n\n// NullFloat64Field returns the sql.NullFloat64 value of the field.\nfunc (row *Row) NullFloat64Field(field Number) sql.NullFloat64 {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call NullFloat64Field for static queries\"))\n\t}\n\tif row.sqlRows == nil {\n\t\trow.fields = append(row.fields, field)\n\t\trow.scanDest = append(row.scanDest, &sql.NullFloat64{})\n\t\treturn sql.NullFloat64{}\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*sql.NullFloat64)\n\treturn *scanDest\n}\n\n// Int returns the int value of the expression.\nfunc (row *Row) Int(format string, values ...any) int {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\treturn int(value)\n\t\tcase float64:\n\t\t\treturn int(value)\n\t\tcase bool:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is bool, not int\", value))\n\t\tcase []byte:\n\t\t\t// Special case: go-mysql-driver returns everything as []byte.\n\t\t\tn, err := strconv.Atoi(string(value))\n\t\t\tif err != nil {\n\t\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is []byte, not int\", value))\n\t\t\t}\n\t\t\treturn n\n\t\tcase string:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%q is string, not int\", value))\n\t\tcase time.Time:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is time.Time, not int\", value))\n\t\tcase nil:\n\t\t\treturn 0\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not int\", value))\n\t\t}\n\t}\n\treturn int(row.NullInt64Field(Expr(format, values...)).Int64)\n}\n\n// IntField returns the int value of the field.\nfunc (row *Row) IntField(field Number) int {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call IntField for static queries\"))\n\t}\n\treturn int(row.NullInt64Field(field).Int64)\n}\n\n// Int64 returns the int64 value of the expression.\nfunc (row *Row) Int64(format string, values ...any) int64 {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\treturn int64(value)\n\t\tcase float64:\n\t\t\treturn int64(value)\n\t\tcase bool:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is bool, not int64\", value))\n\t\tcase []byte:\n\t\t\t// Special case: go-mysql-driver returns everything as []byte.\n\t\t\tn, err := strconv.ParseInt(string(value), 10, 64)\n\t\t\tif err != nil {\n\t\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is []byte, not int64\", value))\n\t\t\t}\n\t\t\treturn n\n\t\tcase string:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%q is string, not int64\", value))\n\t\tcase time.Time:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is time.Time, not int64\", value))\n\t\tcase nil:\n\t\t\treturn 0\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not int64\", value))\n\t\t}\n\t}\n\treturn row.NullInt64Field(Expr(format, values...)).Int64\n}\n\n// Int64Field returns the int64 value of the field.\nfunc (row *Row) Int64Field(field Number) int64 {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call Int64Field for static queries\"))\n\t}\n\treturn row.NullInt64Field(field).Int64\n}\n\n// NullInt64 returns the sql.NullInt64 value of the expression.\nfunc (row *Row) NullInt64(format string, values ...any) sql.NullInt64 {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\treturn sql.NullInt64{Int64: value, Valid: true}\n\t\tcase float64:\n\t\t\treturn sql.NullInt64{Int64: int64(value), Valid: true}\n\t\tcase bool:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is bool, not int64\", value))\n\t\tcase []byte:\n\t\t\t// Special case: go-mysql-driver returns everything as []byte.\n\t\t\tn, err := strconv.ParseInt(string(value), 10, 64)\n\t\t\tif err != nil {\n\t\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is []byte, not int64\", value))\n\t\t\t}\n\t\t\treturn sql.NullInt64{Int64: n, Valid: true}\n\t\tcase string:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%q is string, not int64\", value))\n\t\tcase time.Time:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is time.Time, not int64\", value))\n\t\tcase nil:\n\t\t\treturn sql.NullInt64{}\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not int64\", value))\n\t\t}\n\t}\n\treturn row.NullInt64Field(Expr(format, values...))\n}\n\n// NullInt64Field returns the sql.NullInt64 value of the field.\nfunc (row *Row) NullInt64Field(field Number) sql.NullInt64 {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call NullInt64Field for static queries\"))\n\t}\n\tif row.sqlRows == nil {\n\t\trow.fields = append(row.fields, field)\n\t\trow.scanDest = append(row.scanDest, &sql.NullInt64{})\n\t\treturn sql.NullInt64{}\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*sql.NullInt64)\n\treturn *scanDest\n}\n\n// JSON scans the JSON expression into destPtr.\nfunc (row *Row) JSON(destPtr any, format string, values ...any) {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call JSON for static queries\"))\n\t}\n\trow.json(destPtr, Expr(format, values...), 1)\n}\n\n// JSONField scans the JSON field into destPtr.\nfunc (row *Row) JSONField(destPtr any, field JSON) {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call JSONField for static queries\"))\n\t}\n\trow.json(destPtr, field, 1)\n}\n\nfunc (row *Row) json(destPtr any, field JSON, skip int) {\n\tif row.sqlRows == nil {\n\t\tif reflect.TypeOf(destPtr).Kind() != reflect.Ptr {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"cannot pass in non pointer value (%#v) as destPtr\", destPtr))\n\t\t}\n\t\trow.fields = append(row.fields, field)\n\t\trow.scanDest = append(row.scanDest, &nullBytes{\n\t\t\tdialect:     row.dialect,\n\t\t\tdisplayType: displayTypeString,\n\t\t})\n\t\treturn\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*nullBytes)\n\tif scanDest.valid {\n\t\terr := json.Unmarshal(scanDest.bytes, destPtr)\n\t\tif err != nil {\n\t\t\t_, file, line, _ := runtime.Caller(skip + 1)\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"unmarshaling json %q into %T: %w\", file, line, string(scanDest.bytes), destPtr, err))\n\t\t}\n\t}\n}\n\n// String returns the string value of the expression.\nfunc (row *Row) String(format string, values ...any) string {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is int64, not string\", value))\n\t\tcase float64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is float64, not string\", value))\n\t\tcase bool:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is bool, not string\", value))\n\t\tcase []byte:\n\t\t\treturn string(value)\n\t\tcase string:\n\t\t\treturn value\n\t\tcase time.Time:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is time.Time, not string\", value))\n\t\tcase nil:\n\t\t\treturn \"\"\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not string\", value))\n\t\t}\n\t}\n\treturn row.NullStringField(Expr(format, values...)).String\n}\n\n// String returns the string value of the field.\nfunc (row *Row) StringField(field String) string {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call StringField for static queries\"))\n\t}\n\treturn row.NullStringField(field).String\n}\n\n// NullString returns the sql.NullString value of the expression.\nfunc (row *Row) NullString(format string, values ...any) sql.NullString {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is int64, not string\", value))\n\t\tcase float64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is float64, not string\", value))\n\t\tcase bool:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is bool, not string\", value))\n\t\tcase []byte:\n\t\t\treturn sql.NullString{String: string(value), Valid: true}\n\t\tcase string:\n\t\t\treturn sql.NullString{String: value, Valid: true}\n\t\tcase time.Time:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is time.Time, not string\", value))\n\t\tcase nil:\n\t\t\treturn sql.NullString{}\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not string\", value))\n\t\t}\n\t}\n\treturn row.NullStringField(Expr(format, values...))\n}\n\n// NullStringField returns the sql.NullString value of the field.\nfunc (row *Row) NullStringField(field String) sql.NullString {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call NullStringField for static queries\"))\n\t}\n\tif row.sqlRows == nil {\n\t\trow.fields = append(row.fields, field)\n\t\trow.scanDest = append(row.scanDest, &sql.NullString{})\n\t\treturn sql.NullString{}\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*sql.NullString)\n\treturn *scanDest\n}\n\n// https://github.com/mattn/go-sqlite3/blob/4396a38886da660e403409e35ef4a37906bf0975/sqlite3.go#L209\nvar sqliteTimestampFormats = []string{\n\t\"2006-01-02 15:04:05.999999999-07:00\",\n\t\"2006-01-02T15:04:05.999999999-07:00\",\n\t\"2006-01-02 15:04:05.999999999\",\n\t\"2006-01-02T15:04:05.999999999\",\n\t\"2006-01-02 15:04:05\",\n\t\"2006-01-02T15:04:05\",\n\t\"2006-01-02 15:04\",\n\t\"2006-01-02T15:04\",\n\t\"2006-01-02\",\n}\n\n// Time returns the time.Time value of the expression.\nfunc (row *Row) Time(format string, values ...any) time.Time {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is int64, not time.Time\", value))\n\t\tcase float64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is float64, not time.Time\", value))\n\t\tcase bool:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is bool, not time.Time\", value))\n\t\tcase []byte:\n\t\t\t// Special case: go-mysql-driver returns everything as []byte.\n\t\t\ts := strings.TrimSuffix(string(value), \"Z\")\n\t\t\tfor _, format := range sqliteTimestampFormats {\n\t\t\t\tif t, err := time.ParseInLocation(format, s, time.UTC); err == nil {\n\t\t\t\t\treturn t\n\t\t\t\t}\n\t\t\t}\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is []byte, not time.Time\", value))\n\t\tcase string:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%q is string, not time.Time\", value))\n\t\tcase time.Time:\n\t\t\treturn value\n\t\tcase nil:\n\t\t\treturn time.Time{}\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not time.Time\", value))\n\t\t}\n\t}\n\treturn row.NullTimeField(Expr(format, values...)).Time\n}\n\n// Time returns the time.Time value of the field.\nfunc (row *Row) TimeField(field Time) time.Time {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call TimeField for static queries\"))\n\t}\n\treturn row.NullTimeField(field).Time\n}\n\n// NullTime returns the sql.NullTime value of the expression.\nfunc (row *Row) NullTime(format string, values ...any) sql.NullTime {\n\tif row.queryIsStatic {\n\t\tindex, ok := row.columnIndex[format]\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"column %s does not exist (available columns: %s)\", format, strings.Join(row.columns, \", \")))\n\t\t}\n\t\tvalue := row.values[index]\n\t\tswitch value := value.(type) {\n\t\tcase int64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is int64, not time.Time\", value))\n\t\tcase float64:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is float64, not time.Time\", value))\n\t\tcase bool:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%v is bool, not time.Time\", value))\n\t\tcase []byte:\n\t\t\t// Special case: go-mysql-driver returns everything as []byte.\n\t\t\ts := strings.TrimSuffix(string(value), \"Z\")\n\t\t\tfor _, format := range sqliteTimestampFormats {\n\t\t\t\tif t, err := time.ParseInLocation(format, s, time.UTC); err == nil {\n\t\t\t\t\treturn sql.NullTime{Time: t, Valid: true}\n\t\t\t\t}\n\t\t\t}\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%d is []byte, not time.Time\", value))\n\t\tcase string:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%q is string, not time.Time\", value))\n\t\tcase time.Time:\n\t\t\treturn sql.NullTime{Time: value, Valid: true}\n\t\tcase nil:\n\t\t\treturn sql.NullTime{}\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(callsite(1)+\"%[1]v is %[1]T, not time.Time\", value))\n\t\t}\n\t}\n\treturn row.NullTimeField(Expr(format, values...))\n}\n\n// NullTimeField returns the sql.NullTime value of the field.\nfunc (row *Row) NullTimeField(field Time) sql.NullTime {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call NullTimeField for static queries\"))\n\t}\n\tif row.sqlRows == nil {\n\t\trow.fields = append(row.fields, field)\n\t\trow.scanDest = append(row.scanDest, &sql.NullTime{})\n\t\treturn sql.NullTime{}\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*sql.NullTime)\n\treturn *scanDest\n}\n\n// UUID scans the UUID expression into destPtr.\nfunc (row *Row) UUID(destPtr any, format string, values ...any) {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call UUID for static queries\"))\n\t}\n\trow.uuid(destPtr, Expr(format, values...), 1)\n}\n\n// UUIDField scans the UUID field into destPtr.\nfunc (row *Row) UUIDField(destPtr any, field UUID) {\n\tif row.queryIsStatic {\n\t\tpanic(fmt.Errorf(callsite(1) + \"cannot call UUIDField for static queries\"))\n\t}\n\trow.uuid(destPtr, field, 1)\n}\n\nfunc (row *Row) uuid(destPtr any, field UUID, skip int) {\n\tif row.sqlRows == nil {\n\t\tif _, ok := destPtr.(*[16]byte); !ok {\n\t\t\tif reflect.TypeOf(destPtr).Kind() != reflect.Ptr {\n\t\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"cannot pass in non pointer value (%#v) as destPtr\", destPtr))\n\t\t\t}\n\t\t\tdestValue := reflect.ValueOf(destPtr).Elem()\n\t\t\tif destValue.Kind() != reflect.Array || destValue.Len() != 16 || destValue.Type().Elem().Kind() != reflect.Uint8 {\n\t\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"%T is not a pointer to a [16]byte\", destPtr))\n\t\t\t}\n\t\t}\n\t\trow.fields = append(row.fields, field)\n\t\trow.scanDest = append(row.scanDest, &nullBytes{\n\t\t\tdialect:     row.dialect,\n\t\t\tdisplayType: displayTypeUUID,\n\t\t})\n\t\treturn\n\t}\n\tdefer func() {\n\t\trow.runningIndex++\n\t}()\n\tscanDest := row.scanDest[row.runningIndex].(*nullBytes)\n\tvar err error\n\tvar uuid [16]byte\n\tif len(scanDest.bytes) == 16 {\n\t\tcopy(uuid[:], scanDest.bytes)\n\t} else if len(scanDest.bytes) > 0 {\n\t\tuuid, err = googleuuid.ParseBytes(scanDest.bytes)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(callsite(skip+1)+\"parsing %q as UUID string: %w\", string(scanDest.bytes), err))\n\t\t}\n\t}\n\tif destArrayPtr, ok := destPtr.(*[16]byte); ok {\n\t\tcopy((*destArrayPtr)[:], uuid[:])\n\t\treturn\n\t}\n\tdestValue := reflect.ValueOf(destPtr).Elem()\n\tfor i := 0; i < 16; i++ {\n\t\tdestValue.Index(i).Set(reflect.ValueOf(uuid[i]))\n\t}\n}\n\n// Column keeps track of what the values mapped to what Field in an\n// InsertQuery or SelectQuery.\ntype Column struct {\n\tdialect string\n\t// determines if UPDATE or INSERT\n\tisUpdate bool\n\t// UPDATE\n\tassignments Assignments\n\t// INSERT\n\trowStarted    bool\n\trowEnded      bool\n\tfirstField    string\n\tinsertColumns Fields\n\trowValues     RowValues\n}\n\n// Set maps the value to the Field.\nfunc (col *Column) Set(field Field, value any) {\n\tif field == nil {\n\t\tpanic(fmt.Errorf(callsite(1) + \"setting a nil field\"))\n\t}\n\t// UPDATE mode\n\tif col.isUpdate {\n\t\tcol.assignments = append(col.assignments, Set(field, value))\n\t\treturn\n\t}\n\t// INSERT mode\n\tname := toString(col.dialect, field)\n\tif name == \"\" {\n\t\tpanic(fmt.Errorf(callsite(1) + \"field name is empty\"))\n\t}\n\tif !col.rowStarted {\n\t\tcol.rowStarted = true\n\t\tcol.firstField = name\n\t\tcol.insertColumns = append(col.insertColumns, field)\n\t\tcol.rowValues = append(col.rowValues, RowValue{value})\n\t\treturn\n\t}\n\tif col.rowStarted && name == col.firstField {\n\t\tif !col.rowEnded {\n\t\t\tcol.rowEnded = true\n\t\t}\n\t\t// Start a new RowValue\n\t\tcol.rowValues = append(col.rowValues, RowValue{value})\n\t\treturn\n\t}\n\tif !col.rowEnded {\n\t\tcol.insertColumns = append(col.insertColumns, field)\n\t}\n\t// Append to last RowValue\n\tlast := len(col.rowValues) - 1\n\tcol.rowValues[last] = append(col.rowValues[last], value)\n}\n\n// SetBytes maps the []byte value to the field.\nfunc (col *Column) SetBytes(field Binary, value []byte) { col.Set(field, value) }\n\n// SetBool maps the bool value to the field.\nfunc (col *Column) SetBool(field Boolean, value bool) { col.Set(field, value) }\n\n// SetFloat64 maps the float64 value to the field.\nfunc (col *Column) SetFloat64(field Number, value float64) { col.Set(field, value) }\n\n// SetInt maps the int value to the field.\nfunc (col *Column) SetInt(field Number, value int) { col.Set(field, value) }\n\n// SetInt64 maps the int64 value to the field.\nfunc (col *Column) SetInt64(field Number, value int64) { col.Set(field, value) }\n\n// SetString maps the string value to the field.\nfunc (col *Column) SetString(field String, value string) { col.Set(field, value) }\n\n// SetTime maps the time.Time value to the field.\nfunc (col *Column) SetTime(field Time, value time.Time) { col.Set(field, value) }\n\n// SetArray maps the array value to the field. The value should be []string,\n// []int, []int64, []int32, []float64, []float32 or []bool.\nfunc (col *Column) SetArray(field Array, value any) { col.Set(field, ArrayValue(value)) }\n\n// SetEnum maps the enum value to the field.\nfunc (col *Column) SetEnum(field Enum, value Enumeration) { col.Set(field, EnumValue(value)) }\n\n// SetJSON maps the JSON value to the field. The value should be able to be\n// convertible to JSON using json.Marshal.\nfunc (col *Column) SetJSON(field JSON, value any) { col.Set(field, JSONValue(value)) }\n\n// SetUUID maps the UUID value to the field. The value's type or underlying\n// type should be [16]byte.\nfunc (col *Column) SetUUID(field UUID, value any) { col.Set(field, UUIDValue(value)) }\n\nfunc callsite(skip int) string {\n\t_, file, line, ok := runtime.Caller(skip + 1)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\treturn filepath.Base(file) + \":\" + strconv.Itoa(line) + \": \"\n}\n\ntype displayType int8\n\nconst (\n\tdisplayTypeBinary displayType = iota\n\tdisplayTypeString\n\tdisplayTypeUUID\n)\n\n// nullBytes is used in place of scanning into *[]byte. We use *nullBytes\n// instead of *[]byte because of the displayType field, which determines how to\n// render the value to the user. This is important for logging the query\n// results, because UUIDs/JSON/Arrays are all scanned into bytes but we don't\n// want to display them as bytes (we need to convert them to UUID/JSON/Array\n// strings instead).\ntype nullBytes struct {\n\tbytes       []byte\n\tdialect     string\n\tdisplayType displayType\n\tvalid       bool\n}\n\nfunc (n *nullBytes) Scan(value any) error {\n\tif value == nil {\n\t\tn.bytes, n.valid = nil, false\n\t\treturn nil\n\t}\n\tn.valid = true\n\tswitch value := value.(type) {\n\tcase string:\n\t\tn.bytes = []byte(value)\n\tcase []byte:\n\t\tn.bytes = value\n\tdefault:\n\t\treturn fmt.Errorf(\"unable to convert %#v to bytes\", value)\n\t}\n\treturn nil\n}\n\nfunc (n *nullBytes) Value() (driver.Value, error) {\n\tif !n.valid {\n\t\treturn nil, nil\n\t}\n\tswitch n.displayType {\n\tcase displayTypeString:\n\t\treturn string(n.bytes), nil\n\tcase displayTypeUUID:\n\t\tif n.dialect != \"postgres\" {\n\t\t\treturn n.bytes, nil\n\t\t}\n\t\tvar uuid [16]byte\n\t\tvar buf [36]byte\n\t\tcopy(uuid[:], n.bytes)\n\t\tgoogleuuid.EncodeHex(buf[:], uuid)\n\t\treturn string(buf[:]), nil\n\tdefault:\n\t\treturn n.bytes, nil\n\t}\n}\n"
  },
  {
    "path": "select_query.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n)\n\n// SelectQuery represents an SQL SELECT query.\ntype SelectQuery struct {\n\tDialect string\n\t// WITH\n\tCTEs []CTE\n\t// SELECT\n\tDistinct         bool\n\tSelectFields     []Field\n\tDistinctOnFields []Field\n\t// TOP\n\tLimitTop        any\n\tLimitTopPercent any\n\t// FROM\n\tFromTable Table\n\t// JOIN\n\tJoinTables []JoinTable\n\t// WHERE\n\tWherePredicate Predicate\n\t// GROUP BY\n\tGroupByFields []Field\n\t// HAVING\n\tHavingPredicate Predicate\n\t// WINDOW\n\tNamedWindows []NamedWindow\n\t// ORDER BY\n\tOrderByFields []Field\n\t// LIMIT\n\tLimitRows any\n\t// OFFSET\n\tOffsetRows any\n\t// FETCH NEXT\n\tFetchNextRows any\n\tFetchWithTies bool\n\t// FOR UPDATE | FOR SHARE\n\tLockClause string\n\tLockValues []any\n\t// AS\n\tAlias   string\n\tColumns []string\n}\n\nvar _ interface {\n\tQuery\n\tTable\n\tField\n\tAny\n} = (*SelectQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q SelectQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\tif len(q.SelectFields) == 0 {\n\t\treturn fmt.Errorf(\"SELECT: no fields provided\")\n\t}\n\t// Table Policies\n\tvar policies []Predicate\n\tpolicies, err = appendPolicy(ctx, dialect, policies, q.FromTable)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"FROM %s Policy: %w\", toString(q.Dialect, q.FromTable), err)\n\t}\n\tfor _, joinTable := range q.JoinTables {\n\t\tpolicies, err = appendPolicy(ctx, dialect, policies, joinTable.Table)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s %s Policy: %w\", joinTable.JoinOperator, joinTable.Table, err)\n\t\t}\n\t}\n\tif len(policies) > 0 {\n\t\tif q.WherePredicate != nil {\n\t\t\tpolicies = append(policies, q.WherePredicate)\n\t\t}\n\t\tq.WherePredicate = And(policies...)\n\t}\n\t// WITH\n\tif len(q.CTEs) > 0 {\n\t\terr = writeCTEs(ctx, dialect, buf, args, params, q.CTEs)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"WITH: %w\", err)\n\t\t}\n\t}\n\t// SELECT\n\tbuf.WriteString(\"SELECT \")\n\tif q.LimitTop != nil || q.LimitTopPercent != nil { // TOP\n\t\tif dialect != DialectSQLServer {\n\t\t\treturn fmt.Errorf(\"%s does not support SELECT TOP n\", dialect)\n\t\t}\n\t\tif len(q.OrderByFields) == 0 {\n\t\t\treturn fmt.Errorf(\"sqlserver does not support TOP without ORDER BY\")\n\t\t}\n\t\terr = writeTop(ctx, dialect, buf, args, params, q.LimitTop, q.LimitTopPercent, q.FetchWithTies)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif len(q.DistinctOnFields) > 0 {\n\t\tif dialect != DialectPostgres {\n\t\t\treturn fmt.Errorf(\"%s does not support SELECT DISTINCT ON\", dialect)\n\t\t}\n\t\tif q.Distinct {\n\t\t\treturn fmt.Errorf(\"postgres SELECT cannot be DISTINCT and DISTINCT ON at the same time\")\n\t\t}\n\t\tbuf.WriteString(\"DISTINCT ON (\")\n\t\terr = writeFields(ctx, dialect, buf, args, params, q.DistinctOnFields, false)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"DISTINCT ON: %w\", err)\n\t\t}\n\t\tbuf.WriteString(\") \")\n\t} else if q.Distinct {\n\t\tbuf.WriteString(\"DISTINCT \")\n\t}\n\terr = writeFields(ctx, dialect, buf, args, params, q.SelectFields, true)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"SELECT: %w\", err)\n\t}\n\t// FROM\n\tif q.FromTable != nil {\n\t\tbuf.WriteString(\" FROM \")\n\t\t_, isQuery := q.FromTable.(Query)\n\t\tif isQuery {\n\t\t\tbuf.WriteString(\"(\")\n\t\t}\n\t\terr = q.FromTable.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"FROM: %w\", err)\n\t\t}\n\t\tif isQuery {\n\t\t\tbuf.WriteString(\")\")\n\t\t}\n\t\tif alias := getAlias(q.FromTable); alias != \"\" {\n\t\t\tbuf.WriteString(\" AS \" + QuoteIdentifier(dialect, alias) + quoteTableColumns(dialect, q.FromTable))\n\t\t} else if isQuery && dialect != DialectSQLite {\n\t\t\treturn fmt.Errorf(\"%s FROM subquery must have alias\", dialect)\n\t\t}\n\t}\n\t// JOIN\n\tif len(q.JoinTables) > 0 {\n\t\tif q.FromTable == nil {\n\t\t\treturn fmt.Errorf(\"can't JOIN without a FROM table\")\n\t\t}\n\t\tbuf.WriteString(\" \")\n\t\terr = writeJoinTables(ctx, dialect, buf, args, params, q.JoinTables)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"JOIN: %w\", err)\n\t\t}\n\t}\n\t// WHERE\n\tif q.WherePredicate != nil {\n\t\tbuf.WriteString(\" WHERE \")\n\t\tswitch predicate := q.WherePredicate.(type) {\n\t\tcase VariadicPredicate:\n\t\t\tpredicate.Toplevel = true\n\t\t\terr = predicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"WHERE: %w\", err)\n\t\t\t}\n\t\tdefault:\n\t\t\terr = q.WherePredicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"WHERE: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\t// GROUP BY\n\tif len(q.GroupByFields) > 0 {\n\t\tbuf.WriteString(\" GROUP BY \")\n\t\terr = writeFields(ctx, dialect, buf, args, params, q.GroupByFields, false)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"GROUP BY: %w\", err)\n\t\t}\n\t}\n\t// HAVING\n\tif q.HavingPredicate != nil {\n\t\tbuf.WriteString(\" HAVING \")\n\t\tswitch predicate := q.HavingPredicate.(type) {\n\t\tcase VariadicPredicate:\n\t\t\tpredicate.Toplevel = true\n\t\t\terr = predicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"HAVING: %w\", err)\n\t\t\t}\n\t\tdefault:\n\t\t\terr = q.HavingPredicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"HAVING: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\t// WINDOW\n\tif len(q.NamedWindows) > 0 {\n\t\tbuf.WriteString(\" WINDOW \")\n\t\terr = NamedWindows(q.NamedWindows).WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"WINDOW: %w\", err)\n\t\t}\n\t}\n\t// ORDER BY\n\tif len(q.OrderByFields) > 0 {\n\t\tbuf.WriteString(\" ORDER BY \")\n\t\terr = writeFields(ctx, dialect, buf, args, params, q.OrderByFields, false)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"ORDER BY: %w\", err)\n\t\t}\n\t}\n\t// LIMIT\n\tif q.LimitRows != nil {\n\t\tif dialect == DialectSQLServer {\n\t\t\treturn fmt.Errorf(\"sqlserver does not support LIMIT\")\n\t\t}\n\t\tbuf.WriteString(\" LIMIT \")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, q.LimitRows)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"LIMIT: %w\", err)\n\t\t}\n\t}\n\t// OFFSET\n\tif q.OffsetRows != nil {\n\t\tif dialect == DialectSQLServer {\n\t\t\tif len(q.OrderByFields) == 0 {\n\t\t\t\treturn fmt.Errorf(\"sqlserver does not support OFFSET without ORDER BY\")\n\t\t\t}\n\t\t\tif q.LimitTop != nil || q.LimitTopPercent != nil {\n\t\t\t\treturn fmt.Errorf(\"sqlserver does not support OFFSET with TOP\")\n\t\t\t}\n\t\t}\n\t\tbuf.WriteString(\" OFFSET \")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, q.OffsetRows)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"OFFSET: %w\", err)\n\t\t}\n\t\tif dialect == DialectSQLServer {\n\t\t\tbuf.WriteString(\" ROWS\")\n\t\t}\n\t}\n\t// FETCH NEXT\n\tif q.FetchNextRows != nil {\n\t\tswitch dialect {\n\t\tcase DialectPostgres:\n\t\t\tif q.LimitRows != nil {\n\t\t\t\treturn fmt.Errorf(\"postgres does not allow FETCH NEXT with LIMIT\")\n\t\t\t}\n\t\tcase DialectSQLServer:\n\t\t\tif q.LimitTop != nil || q.LimitTopPercent != nil {\n\t\t\t\treturn fmt.Errorf(\"sqlserver does not allow FETCH NEXT with TOP\")\n\t\t\t}\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"%s does not support FETCH NEXT\", dialect)\n\t\t}\n\t\tbuf.WriteString(\" FETCH NEXT \")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, q.FetchNextRows)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"FETCH NEXT: %w\", err)\n\t\t}\n\t\tbuf.WriteString(\" ROWS \")\n\t\tif q.FetchWithTies {\n\t\t\tif dialect == DialectSQLServer {\n\t\t\t\treturn fmt.Errorf(\"sqlserver WITH TIES only works with TOP\")\n\t\t\t}\n\t\t\tif len(q.OrderByFields) == 0 {\n\t\t\t\treturn fmt.Errorf(\"%s WITH TIES cannot be used without ORDER BY\", dialect)\n\t\t\t}\n\t\t\tbuf.WriteString(\"WITH TIES\")\n\t\t} else {\n\t\t\tbuf.WriteString(\"ONLY\")\n\t\t}\n\t}\n\t// FOR UPDATE | FOR SHARE\n\tif q.LockClause != \"\" {\n\t\tbuf.WriteString(\" \")\n\t\terr = Writef(ctx, dialect, buf, args, params, q.LockClause, q.LockValues)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Select creates a new SelectQuery.\nfunc Select(fields ...Field) SelectQuery {\n\treturn SelectQuery{SelectFields: fields}\n}\n\n// SelectDistinct creates a new SelectQuery.\nfunc SelectDistinct(fields ...Field) SelectQuery {\n\treturn SelectQuery{\n\t\tSelectFields: fields,\n\t\tDistinct:     true,\n\t}\n}\n\n// SelectOne creates a new SelectQuery.\nfunc SelectOne() SelectQuery {\n\treturn SelectQuery{SelectFields: Fields{Expr(\"1\")}}\n}\n\n// From creates a new SelectQuery.\nfunc From(table Table) SelectQuery {\n\treturn SelectQuery{FromTable: table}\n}\n\n// Select appends to the SelectFields in the SelectQuery.\nfunc (q SelectQuery) Select(fields ...Field) SelectQuery {\n\tq.SelectFields = append(q.SelectFields, fields...)\n\treturn q\n}\n\n// SelectDistinct sets the SelectFields in the SelectQuery.\nfunc (q SelectQuery) SelectDistinct(fields ...Field) SelectQuery {\n\tq.SelectFields = fields\n\tq.Distinct = true\n\treturn q\n}\n\n// SelectOne sets the SelectQuery to SELECT 1.\nfunc (q SelectQuery) SelectOne(fields ...Field) SelectQuery {\n\tq.SelectFields = Fields{Expr(\"1\")}\n\treturn q\n}\n\n// From sets the FromTable field in the SelectQuery.\nfunc (q SelectQuery) From(table Table) SelectQuery {\n\tq.FromTable = table\n\treturn q\n}\n\n// Join joins a new Table to the SelectQuery.\nfunc (q SelectQuery) Join(table Table, predicates ...Predicate) SelectQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the SelectQuery.\nfunc (q SelectQuery) LeftJoin(table Table, predicates ...Predicate) SelectQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the SelectQuery.\nfunc (q SelectQuery) CrossJoin(table Table) SelectQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the SelectQuery with a custom join operator.\nfunc (q SelectQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) SelectQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// JoinUsing joins a new Table to the SelectQuery with the USING operator.\nfunc (q SelectQuery) JoinUsing(table Table, fields ...Field) SelectQuery {\n\tq.JoinTables = append(q.JoinTables, JoinUsing(table, fields...))\n\treturn q\n}\n\n// Where appends to the WherePredicate field in the SelectQuery.\nfunc (q SelectQuery) Where(predicates ...Predicate) SelectQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// GroupBy appends to the GroupByFields field in the SelectQuery.\nfunc (q SelectQuery) GroupBy(fields ...Field) SelectQuery {\n\tq.GroupByFields = append(q.GroupByFields, fields...)\n\treturn q\n}\n\n// Having appends to the HavingPredicate field in the SelectQuery.\nfunc (q SelectQuery) Having(predicates ...Predicate) SelectQuery {\n\tq.HavingPredicate = appendPredicates(q.HavingPredicate, predicates)\n\treturn q\n}\n\n// OrderBy appends to the OrderByFields field in the SelectQuery.\nfunc (q SelectQuery) OrderBy(fields ...Field) SelectQuery {\n\tq.OrderByFields = append(q.OrderByFields, fields...)\n\treturn q\n}\n\n// Limit sets the LimitRows field in the SelectQuery.\nfunc (q SelectQuery) Limit(limit any) SelectQuery {\n\tq.LimitRows = limit\n\treturn q\n}\n\n// Offset sets the OffsetRows field in the SelectQuery.\nfunc (q SelectQuery) Offset(offset any) SelectQuery {\n\tq.OffsetRows = offset\n\treturn q\n}\n\n// As returns a new SelectQuery with the table alias (and optionally column\n// aliases).\nfunc (q SelectQuery) As(alias string, columns ...string) SelectQuery {\n\tq.Alias = alias\n\tq.Columns = columns\n\treturn q\n}\n\n// Field returns a new field qualified by the SelectQuery's alias.\nfunc (q SelectQuery) Field(name string) AnyField {\n\treturn NewAnyField(name, TableStruct{alias: q.Alias})\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q SelectQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\tif len(q.SelectFields) == 0 {\n\t\tq.SelectFields = fields\n\t\treturn q, true\n\t}\n\treturn q, false\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q SelectQuery) GetFetchableFields() []Field {\n\treturn q.SelectFields\n}\n\n// GetDialect implements the Query interface.\nfunc (q SelectQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q SelectQuery) SetDialect(dialect string) SelectQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// GetAlias returns the alias of the SelectQuery.\nfunc (q SelectQuery) GetAlias() string { return q.Alias }\n\n// GetColumns returns the column aliases of the SelectQuery.\nfunc (q SelectQuery) GetColumns() []string { return q.Columns }\n\n// IsTable implements the Table interface.\nfunc (q SelectQuery) IsTable() {}\n\n// IsField implements the Field interface.\nfunc (q SelectQuery) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (q SelectQuery) IsArray() {}\n\n// IsBinary implements the Binary interface.\nfunc (q SelectQuery) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (q SelectQuery) IsBoolean() {}\n\n// IsEnum implements the Enum interface.\nfunc (q SelectQuery) IsEnum() {}\n\n// IsJSON implements the JSON interface.\nfunc (q SelectQuery) IsJSON() {}\n\n// IsNumber implements the Number interface.\nfunc (q SelectQuery) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (q SelectQuery) IsString() {}\n\n// IsTime implements the Time interface.\nfunc (q SelectQuery) IsTime() {}\n\n// IsUUID implements the UUID interface.\nfunc (q SelectQuery) IsUUID() {}\n\n// SQLiteSelectQuery represents an SQLite SELECT query.\ntype SQLiteSelectQuery SelectQuery\n\nvar _ interface {\n\tQuery\n\tTable\n\tField\n\tAny\n} = (*SQLiteSelectQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q SQLiteSelectQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn SelectQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// Select creates a new SQLiteSelectQuery.\nfunc (b sqliteQueryBuilder) Select(fields ...Field) SQLiteSelectQuery {\n\treturn SQLiteSelectQuery{\n\t\tDialect:      DialectSQLite,\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: fields,\n\t}\n}\n\n// SelectDistinct creates a new SQLiteSelectQuery.\nfunc (b sqliteQueryBuilder) SelectDistinct(fields ...Field) SQLiteSelectQuery {\n\treturn SQLiteSelectQuery{\n\t\tDialect:      DialectSQLite,\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: fields,\n\t\tDistinct:     true,\n\t}\n}\n\n// SelectOne creates a new SQLiteSelectQuery.\nfunc (b sqliteQueryBuilder) SelectOne() SQLiteSelectQuery {\n\treturn SQLiteSelectQuery{\n\t\tDialect:      DialectSQLite,\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: Fields{Expr(\"1\")},\n\t}\n}\n\n// From creates a new SQLiteSelectQuery.\nfunc (b sqliteQueryBuilder) From(table Table) SQLiteSelectQuery {\n\treturn SQLiteSelectQuery{\n\t\tDialect:   DialectSQLite,\n\t\tCTEs:      b.ctes,\n\t\tFromTable: table,\n\t}\n}\n\n// Select appends to the SelectFields in the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) Select(fields ...Field) SQLiteSelectQuery {\n\tq.SelectFields = append(q.SelectFields, fields...)\n\treturn q\n}\n\n// SelectDistinct sets the SelectFields in the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) SelectDistinct(fields ...Field) SQLiteSelectQuery {\n\tq.SelectFields = fields\n\tq.Distinct = true\n\treturn q\n}\n\n// SelectOne sets the SQLiteSelectQuery to SELECT 1.\nfunc (q SQLiteSelectQuery) SelectOne(fields ...Field) SQLiteSelectQuery {\n\tq.SelectFields = Fields{Expr(\"1\")}\n\treturn q\n}\n\n// From sets the FromTable field in the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) From(table Table) SQLiteSelectQuery {\n\tq.FromTable = table\n\treturn q\n}\n\n// Join joins a new Table to the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) Join(table Table, predicates ...Predicate) SQLiteSelectQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) LeftJoin(table Table, predicates ...Predicate) SQLiteSelectQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) CrossJoin(table Table) SQLiteSelectQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the SQLiteSelectQuery with a custom join\n// operator.\nfunc (q SQLiteSelectQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) SQLiteSelectQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// JoinUsing joins a new Table to the SQLiteSelectQuery with the USING operator.\nfunc (q SQLiteSelectQuery) JoinUsing(table Table, fields ...Field) SQLiteSelectQuery {\n\tq.JoinTables = append(q.JoinTables, JoinUsing(table, fields...))\n\treturn q\n}\n\n// Where appends to the WherePredicate field in the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) Where(predicates ...Predicate) SQLiteSelectQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// GroupBy appends to the GroupByFields field in the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) GroupBy(fields ...Field) SQLiteSelectQuery {\n\tq.GroupByFields = append(q.GroupByFields, fields...)\n\treturn q\n}\n\n// Having appends to the HavingPredicate field in the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) Having(predicates ...Predicate) SQLiteSelectQuery {\n\tq.HavingPredicate = appendPredicates(q.HavingPredicate, predicates)\n\treturn q\n}\n\n// OrderBy appends to the OrderByFields field in the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) OrderBy(fields ...Field) SQLiteSelectQuery {\n\tq.OrderByFields = append(q.OrderByFields, fields...)\n\treturn q\n}\n\n// Limit sets the LimitRows field in the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) Limit(limit any) SQLiteSelectQuery {\n\tq.LimitRows = limit\n\treturn q\n}\n\n// Offset sets the OffsetRows field in the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) Offset(offset any) SQLiteSelectQuery {\n\tq.OffsetRows = offset\n\treturn q\n}\n\n// As returns a new SQLiteSelectQuery with the table alias (and optionally\n// column aliases).\nfunc (q SQLiteSelectQuery) As(alias string, columns ...string) SQLiteSelectQuery {\n\tq.Alias = alias\n\tq.Columns = columns\n\treturn q\n}\n\n// Field returns a new field qualified by the SQLiteSelectQuery's alias.\nfunc (q SQLiteSelectQuery) Field(name string) AnyField {\n\treturn NewAnyField(name, TableStruct{alias: q.Alias})\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q SQLiteSelectQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\tif len(q.SelectFields) == 0 {\n\t\tq.SelectFields = fields\n\t\treturn q, true\n\t}\n\treturn q, false\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q SQLiteSelectQuery) GetFetchableFields() []Field {\n\treturn q.SelectFields\n}\n\n// GetDialect implements the Query interface.\nfunc (q SQLiteSelectQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q SQLiteSelectQuery) SetDialect(dialect string) SQLiteSelectQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// GetAlias returns the alias of the SQLiteSelectQuery.\nfunc (q SQLiteSelectQuery) GetAlias() string { return q.Alias }\n\n// IsTable implements the Table interface.\nfunc (q SQLiteSelectQuery) IsTable() {}\n\n// IsField implements the Field interface.\nfunc (q SQLiteSelectQuery) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (q SQLiteSelectQuery) IsArray() {}\n\n// IsBinary implements the Binary interface.\nfunc (q SQLiteSelectQuery) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (q SQLiteSelectQuery) IsBoolean() {}\n\n// IsEnum implements the Enum interface.\nfunc (q SQLiteSelectQuery) IsEnum() {}\n\n// IsJSON implements the JSON interface.\nfunc (q SQLiteSelectQuery) IsJSON() {}\n\n// IsNumber implements the Number interface.\nfunc (q SQLiteSelectQuery) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (q SQLiteSelectQuery) IsString() {}\n\n// IsTime implements the Time interface.\nfunc (q SQLiteSelectQuery) IsTime() {}\n\n// IsUUID implements the UUID interface.\nfunc (q SQLiteSelectQuery) IsUUID() {}\n\n// PostgresSelectQuery represents a Postgres SELECT query.\ntype PostgresSelectQuery SelectQuery\n\nvar _ interface {\n\tQuery\n\tTable\n\tField\n\tAny\n} = (*PostgresSelectQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q PostgresSelectQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn SelectQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// Select creates a new PostgresSelectQuery.\nfunc (b postgresQueryBuilder) Select(fields ...Field) PostgresSelectQuery {\n\tq := PostgresSelectQuery{\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: fields,\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectPostgres\n\t}\n\treturn q\n}\n\n// SelectDistinct creates a new PostgresSelectQuery.\nfunc (b postgresQueryBuilder) SelectDistinct(fields ...Field) PostgresSelectQuery {\n\tq := PostgresSelectQuery{\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: fields,\n\t\tDistinct:     true,\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectPostgres\n\t}\n\treturn q\n}\n\n// SelectOne creates a new PostgresSelectQuery.\nfunc (b postgresQueryBuilder) SelectOne() PostgresSelectQuery {\n\tq := PostgresSelectQuery{\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: Fields{Expr(\"1\")},\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectPostgres\n\t}\n\treturn q\n}\n\n// From creates a new PostgresSelectQuery.\nfunc (b postgresQueryBuilder) From(table Table) PostgresSelectQuery {\n\tq := PostgresSelectQuery{\n\t\tCTEs:      b.ctes,\n\t\tFromTable: table,\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectPostgres\n\t}\n\treturn q\n}\n\n// Select appends to the SelectFields in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) Select(fields ...Field) PostgresSelectQuery {\n\tq.SelectFields = append(q.SelectFields, fields...)\n\treturn q\n}\n\n// SelectDistinct sets the SelectFields in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) SelectDistinct(fields ...Field) PostgresSelectQuery {\n\tq.SelectFields = fields\n\tq.Distinct = true\n\treturn q\n}\n\n// DistinctOn sets the DistinctOnFields in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) DistinctOn(fields ...Field) PostgresSelectQuery {\n\tq.DistinctOnFields = fields\n\treturn q\n}\n\n// SelectOne sets the PostgresSelectQuery to SELECT 1.\nfunc (q PostgresSelectQuery) SelectOne(fields ...Field) PostgresSelectQuery {\n\tq.SelectFields = Fields{Expr(\"1\")}\n\treturn q\n}\n\n// From sets the FromTable field in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) From(table Table) PostgresSelectQuery {\n\tq.FromTable = table\n\treturn q\n}\n\n// Join joins a new Table to the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) Join(table Table, predicates ...Predicate) PostgresSelectQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) LeftJoin(table Table, predicates ...Predicate) PostgresSelectQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// FullJoin full joins a new Table to the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) FullJoin(table Table, predicates ...Predicate) PostgresSelectQuery {\n\tq.JoinTables = append(q.JoinTables, FullJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) CrossJoin(table Table) PostgresSelectQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the PostgresSelectQuery with a custom join\n// operator.\nfunc (q PostgresSelectQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) PostgresSelectQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// JoinUsing joins a new Table to the PostgresSelectQuery with the USING operator.\nfunc (q PostgresSelectQuery) JoinUsing(table Table, fields ...Field) PostgresSelectQuery {\n\tq.JoinTables = append(q.JoinTables, JoinUsing(table, fields...))\n\treturn q\n}\n\n// Where appends to the WherePredicate field in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) Where(predicates ...Predicate) PostgresSelectQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// GroupBy appends to the GroupByFields field in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) GroupBy(fields ...Field) PostgresSelectQuery {\n\tq.GroupByFields = append(q.GroupByFields, fields...)\n\treturn q\n}\n\n// Having appends to the HavingPredicate field in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) Having(predicates ...Predicate) PostgresSelectQuery {\n\tq.HavingPredicate = appendPredicates(q.HavingPredicate, predicates)\n\treturn q\n}\n\n// OrderBy appends to the OrderByFields field in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) OrderBy(fields ...Field) PostgresSelectQuery {\n\tq.OrderByFields = append(q.OrderByFields, fields...)\n\treturn q\n}\n\n// Limit sets the LimitRows field in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) Limit(limit any) PostgresSelectQuery {\n\tq.LimitRows = limit\n\treturn q\n}\n\n// Offset sets the OffsetRows field in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) Offset(offset any) PostgresSelectQuery {\n\tq.OffsetRows = offset\n\treturn q\n}\n\n// FetchNext sets the FetchNextRows field in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) FetchNext(n any) PostgresSelectQuery {\n\tq.FetchNextRows = n\n\treturn q\n}\n\n// WithTies enables the FetchWithTies field in the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) WithTies() PostgresSelectQuery {\n\tq.FetchWithTies = true\n\treturn q\n}\n\n// LockRows sets the lock clause of the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) LockRows(lockClause string, lockValues ...any) PostgresSelectQuery {\n\tq.LockClause = lockClause\n\tq.LockValues = lockValues\n\treturn q\n}\n\n// As returns a new PostgresSelectQuery with the table alias (and optionally\n// column aliases).\nfunc (q PostgresSelectQuery) As(alias string, columns ...string) PostgresSelectQuery {\n\tq.Alias = alias\n\tq.Columns = columns\n\treturn q\n}\n\n// Field returns a new field qualified by the PostgresSelectQuery's alias.\nfunc (q PostgresSelectQuery) Field(name string) AnyField {\n\treturn NewAnyField(name, TableStruct{alias: q.Alias})\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q PostgresSelectQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\tif len(q.SelectFields) == 0 {\n\t\tq.SelectFields = fields\n\t\treturn q, true\n\t}\n\treturn q, false\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q PostgresSelectQuery) GetFetchableFields() []Field {\n\treturn q.SelectFields\n}\n\n// GetDialect implements the Query interface.\nfunc (q PostgresSelectQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q PostgresSelectQuery) SetDialect(dialect string) PostgresSelectQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// GetAlias returns the alias of the PostgresSelectQuery.\nfunc (q PostgresSelectQuery) GetAlias() string { return q.Alias }\n\n// IsTable implements the Table interface.\nfunc (q PostgresSelectQuery) IsTable() {}\n\n// IsField implements the Field interface.\nfunc (q PostgresSelectQuery) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (q PostgresSelectQuery) IsArray() {}\n\n// IsBinary implements the Binary interface.\nfunc (q PostgresSelectQuery) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (q PostgresSelectQuery) IsBoolean() {}\n\n// IsEnum implements the Enum interface.\nfunc (q PostgresSelectQuery) IsEnum() {}\n\n// IsJSON implements the JSON interface.\nfunc (q PostgresSelectQuery) IsJSON() {}\n\n// IsNumber implements the Number interface.\nfunc (q PostgresSelectQuery) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (q PostgresSelectQuery) IsString() {}\n\n// IsTime implements the Time interface.\nfunc (q PostgresSelectQuery) IsTime() {}\n\n// IsUUID implements the UUID interface.\nfunc (q PostgresSelectQuery) IsUUID() {}\n\n// MySQLSelectQuery represents a MySQL SELECT query.\ntype MySQLSelectQuery SelectQuery\n\nvar _ interface {\n\tQuery\n\tTable\n\tField\n\tAny\n} = (*MySQLSelectQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q MySQLSelectQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn SelectQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// Select creates a new MySQLSelectQuery.\nfunc (b mysqlQueryBuilder) Select(fields ...Field) MySQLSelectQuery {\n\tq := MySQLSelectQuery{\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: fields,\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectMySQL\n\t}\n\treturn q\n}\n\n// SelectDistinct creates a new MySQLSelectQuery.\nfunc (b mysqlQueryBuilder) SelectDistinct(fields ...Field) MySQLSelectQuery {\n\tq := MySQLSelectQuery{\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: fields,\n\t\tDistinct:     true,\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectMySQL\n\t}\n\treturn q\n}\n\n// SelectOne creates a new MySQLSelectQuery.\nfunc (b mysqlQueryBuilder) SelectOne() MySQLSelectQuery {\n\tq := MySQLSelectQuery{\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: Fields{Expr(\"1\")},\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectMySQL\n\t}\n\treturn q\n}\n\n// From creates a new MySQLSelectQuery.\nfunc (b mysqlQueryBuilder) From(table Table) MySQLSelectQuery {\n\tq := MySQLSelectQuery{\n\t\tCTEs:      b.ctes,\n\t\tFromTable: table,\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectMySQL\n\t}\n\treturn q\n}\n\n// Select appends to the SelectFields in the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) Select(fields ...Field) MySQLSelectQuery {\n\tq.SelectFields = append(q.SelectFields, fields...)\n\treturn q\n}\n\n// SelectDistinct sets the SelectFields in the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) SelectDistinct(fields ...Field) MySQLSelectQuery {\n\tq.SelectFields = fields\n\tq.Distinct = true\n\treturn q\n}\n\n// SelectOne sets the MySQLSelectQuery to SELECT 1.\nfunc (q MySQLSelectQuery) SelectOne(fields ...Field) MySQLSelectQuery {\n\tq.SelectFields = Fields{Expr(\"1\")}\n\treturn q\n}\n\n// From sets the FromTable field in the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) From(table Table) MySQLSelectQuery {\n\tq.FromTable = table\n\treturn q\n}\n\n// Join joins a new Table to the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) Join(table Table, predicates ...Predicate) MySQLSelectQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) LeftJoin(table Table, predicates ...Predicate) MySQLSelectQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// FullJoin full joins a new Table to the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) FullJoin(table Table, predicates ...Predicate) MySQLSelectQuery {\n\tq.JoinTables = append(q.JoinTables, FullJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) CrossJoin(table Table) MySQLSelectQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the MySQLSelectQuery with a custom join\n// operator.\nfunc (q MySQLSelectQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) MySQLSelectQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// JoinUsing joins a new Table to the MySQLSelectQuery with the USING operator.\nfunc (q MySQLSelectQuery) JoinUsing(table Table, fields ...Field) MySQLSelectQuery {\n\tq.JoinTables = append(q.JoinTables, JoinUsing(table, fields...))\n\treturn q\n}\n\n// Where appends to the WherePredicate field in the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) Where(predicates ...Predicate) MySQLSelectQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// GroupBy appends to the GroupByFields field in the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) GroupBy(fields ...Field) MySQLSelectQuery {\n\tq.GroupByFields = append(q.GroupByFields, fields...)\n\treturn q\n}\n\n// Having appends to the HavingPredicate field in the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) Having(predicates ...Predicate) MySQLSelectQuery {\n\tq.HavingPredicate = appendPredicates(q.HavingPredicate, predicates)\n\treturn q\n}\n\n// OrderBy appends to the OrderByFields field in the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) OrderBy(fields ...Field) MySQLSelectQuery {\n\tq.OrderByFields = append(q.OrderByFields, fields...)\n\treturn q\n}\n\n// Limit sets the LimitRows field in the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) Limit(limit any) MySQLSelectQuery {\n\tq.LimitRows = limit\n\treturn q\n}\n\n// Offset sets the OffsetRows field in the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) Offset(offset any) MySQLSelectQuery {\n\tq.OffsetRows = offset\n\treturn q\n}\n\n// LockRows sets the lock clause of the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) LockRows(lockClause string, lockValues ...any) MySQLSelectQuery {\n\tq.LockClause = lockClause\n\tq.LockValues = lockValues\n\treturn q\n}\n\n// As returns a new MySQLSelectQuery with the table alias (and optionally\n// column aliases).\nfunc (q MySQLSelectQuery) As(alias string, columns ...string) MySQLSelectQuery {\n\tq.Alias = alias\n\tq.Columns = columns\n\treturn q\n}\n\n// Field returns a new field qualified by the MySQLSelectQuery's alias.\nfunc (q MySQLSelectQuery) Field(name string) AnyField {\n\treturn NewAnyField(name, TableStruct{alias: q.Alias})\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q MySQLSelectQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\tif len(q.SelectFields) == 0 {\n\t\tq.SelectFields = fields\n\t\treturn q, true\n\t}\n\treturn q, false\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q MySQLSelectQuery) GetFetchableFields() []Field {\n\treturn q.SelectFields\n}\n\n// GetDialect implements the Query interface.\nfunc (q MySQLSelectQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q MySQLSelectQuery) SetDialect(dialect string) MySQLSelectQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// GetAlias returns the alias of the MySQLSelectQuery.\nfunc (q MySQLSelectQuery) GetAlias() string { return q.Alias }\n\n// IsTable implements the Table interface.\nfunc (q MySQLSelectQuery) IsTable() {}\n\n// IsField implements the Field interface.\nfunc (q MySQLSelectQuery) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (q MySQLSelectQuery) IsArray() {}\n\n// IsBinary implements the Binary interface.\nfunc (q MySQLSelectQuery) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (q MySQLSelectQuery) IsBoolean() {}\n\n// IsEnum implements the Enum interface.\nfunc (q MySQLSelectQuery) IsEnum() {}\n\n// IsJSON implements the JSON interface.\nfunc (q MySQLSelectQuery) IsJSON() {}\n\n// IsNumber implements the Number interface.\nfunc (q MySQLSelectQuery) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (q MySQLSelectQuery) IsString() {}\n\n// IsTime implements the Time interface.\nfunc (q MySQLSelectQuery) IsTime() {}\n\n// IsUUID implements the UUID interface.\nfunc (q MySQLSelectQuery) IsUUID() {}\n\n// SQLServerSelectQuery represents an SQL Server SELECT query.\ntype SQLServerSelectQuery SelectQuery\n\nvar _ interface {\n\tQuery\n\tTable\n\tField\n\tAny\n} = (*SQLServerSelectQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q SQLServerSelectQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn SelectQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// Select creates a new SQLServerSelectQuery.\nfunc (b sqlserverQueryBuilder) Select(fields ...Field) SQLServerSelectQuery {\n\tq := SQLServerSelectQuery{\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: fields,\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectSQLServer\n\t}\n\treturn q\n}\n\n// SelectDistinct creates a new SQLServerSelectQuery.\nfunc (b sqlserverQueryBuilder) SelectDistinct(fields ...Field) SQLServerSelectQuery {\n\tq := SQLServerSelectQuery{\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: fields,\n\t\tDistinct:     true,\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectSQLServer\n\t}\n\treturn q\n}\n\n// SelectOne creates a new SQLServerSelectQuery.\nfunc (b sqlserverQueryBuilder) SelectOne() SQLServerSelectQuery {\n\tq := SQLServerSelectQuery{\n\t\tCTEs:         b.ctes,\n\t\tSelectFields: Fields{Expr(\"1\")},\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectSQLServer\n\t}\n\treturn q\n}\n\n// From creates a new SQLServerSelectQuery.\nfunc (b sqlserverQueryBuilder) From(table Table) SQLServerSelectQuery {\n\tq := SQLServerSelectQuery{\n\t\tCTEs:      b.ctes,\n\t\tFromTable: table,\n\t}\n\tif q.Dialect == \"\" {\n\t\tq.Dialect = DialectSQLServer\n\t}\n\treturn q\n}\n\n// Select appends to the SelectFields in the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) Select(fields ...Field) SQLServerSelectQuery {\n\tq.SelectFields = append(q.SelectFields, fields...)\n\treturn q\n}\n\n// SelectDistinct sets the SelectFields in the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) SelectDistinct(fields ...Field) SQLServerSelectQuery {\n\tq.SelectFields = fields\n\tq.Distinct = true\n\treturn q\n}\n\n// SelectOne sets the SQLServerSelectQuery to SELECT 1.\nfunc (q SQLServerSelectQuery) SelectOne(fields ...Field) SQLServerSelectQuery {\n\tq.SelectFields = Fields{Expr(\"1\")}\n\treturn q\n}\n\n// Top sets the LimitTop field of the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) Top(limit any) SQLServerSelectQuery {\n\tq.LimitTop = limit\n\treturn q\n}\n\n// Top sets the LimitTopPercent field of the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) TopPercent(percentLimit any) SQLServerSelectQuery {\n\tq.LimitTopPercent = percentLimit\n\treturn q\n}\n\n// From sets the FromTable field in the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) From(table Table) SQLServerSelectQuery {\n\tq.FromTable = table\n\treturn q\n}\n\n// Join joins a new Table to the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) Join(table Table, predicates ...Predicate) SQLServerSelectQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) LeftJoin(table Table, predicates ...Predicate) SQLServerSelectQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// FullJoin full joins a new Table to the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) FullJoin(table Table, predicates ...Predicate) SQLServerSelectQuery {\n\tq.JoinTables = append(q.JoinTables, FullJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) CrossJoin(table Table) SQLServerSelectQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the SQLServerSelectQuery with a custom join\n// operator.\nfunc (q SQLServerSelectQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) SQLServerSelectQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// Where appends to the WherePredicate field in the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) Where(predicates ...Predicate) SQLServerSelectQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// GroupBy appends to the GroupByFields field in the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) GroupBy(fields ...Field) SQLServerSelectQuery {\n\tq.GroupByFields = append(q.GroupByFields, fields...)\n\treturn q\n}\n\n// Having appends to the HavingPredicate field in the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) Having(predicates ...Predicate) SQLServerSelectQuery {\n\tq.HavingPredicate = appendPredicates(q.HavingPredicate, predicates)\n\treturn q\n}\n\n// OrderBy appends to the OrderByFields field in the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) OrderBy(fields ...Field) SQLServerSelectQuery {\n\tq.OrderByFields = append(q.OrderByFields, fields...)\n\treturn q\n}\n\n// Offset sets the OffsetRows field in the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) Offset(offset any) SQLServerSelectQuery {\n\tq.OffsetRows = offset\n\treturn q\n}\n\n// FetchNext sets the FetchNextRows field in the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) FetchNext(n any) SQLServerSelectQuery {\n\tq.FetchNextRows = n\n\treturn q\n}\n\n// WithTies enables the FetchWithTies field in the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) WithTies() SQLServerSelectQuery {\n\tq.FetchWithTies = true\n\treturn q\n}\n\n// As returns a new SQLServerSelectQuery with the table alias (and optionally\n// column aliases).\nfunc (q SQLServerSelectQuery) As(alias string, columns ...string) SQLServerSelectQuery {\n\tq.Alias = alias\n\tq.Columns = columns\n\treturn q\n}\n\n// Field returns a new field qualified by the SQLServerSelectQuery's alias.\nfunc (q SQLServerSelectQuery) Field(name string) AnyField {\n\treturn NewAnyField(name, TableStruct{alias: q.Alias})\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q SQLServerSelectQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\tif len(q.SelectFields) == 0 {\n\t\tq.SelectFields = fields\n\t\treturn q, true\n\t}\n\treturn q, false\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q SQLServerSelectQuery) GetFetchableFields() []Field {\n\treturn q.SelectFields\n}\n\n// GetDialect implements the Query interface.\nfunc (q SQLServerSelectQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q SQLServerSelectQuery) SetDialect(dialect string) SQLServerSelectQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// GetAlias returns the alias of the SQLServerSelectQuery.\nfunc (q SQLServerSelectQuery) GetAlias() string { return q.Alias }\n\n// IsTable implements the Table interface.\nfunc (q SQLServerSelectQuery) IsTable() {}\n\n// IsField implements the Field interface.\nfunc (q SQLServerSelectQuery) IsField() {}\n\n// IsArray implements the Array interface.\nfunc (q SQLServerSelectQuery) IsArray() {}\n\n// IsBinary implements the Binary interface.\nfunc (q SQLServerSelectQuery) IsBinary() {}\n\n// IsBoolean implements the Boolean interface.\nfunc (q SQLServerSelectQuery) IsBoolean() {}\n\n// IsEnum implements the Enum interface.\nfunc (q SQLServerSelectQuery) IsEnum() {}\n\n// IsJSON implements the JSON interface.\nfunc (q SQLServerSelectQuery) IsJSON() {}\n\n// IsNumber implements the Number interface.\nfunc (q SQLServerSelectQuery) IsNumber() {}\n\n// IsString implements the String interface.\nfunc (q SQLServerSelectQuery) IsString() {}\n\n// IsTime implements the Time interface.\nfunc (q SQLServerSelectQuery) IsTime() {}\n\n// IsUUID implements the UUID interface.\nfunc (q SQLServerSelectQuery) IsUUID() {}\n"
  },
  {
    "path": "select_query_test.go",
    "content": "package sq\n\nimport (\n\t\"testing\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n)\n\nfunc TestSQLiteSelectQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := SQLite.SelectOne().From(a).SetDialect(\"lorem ipsum\").As(\"q1\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif diff := testutil.Diff(q1.GetAlias(), \"q1\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.ACTOR_ID})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.SelectFields = q1.SelectFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.ACTOR_ID})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"subquery, cte and joins\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\tsubquery := SQLite.Select(Expr(\"*\")).From(a).As(\"subquery\")\n\t\tcte := NewCTE(\"cte\", nil, SQLite.From(a).Select(Expr(\"*\")))\n\t\ttt.item = SQLite.\n\t\t\tWith(cte).\n\t\t\tFrom(a).From(a).\n\t\t\tJoin(subquery, Eq(subquery.Field(\"actor_id\"), a.ACTOR_ID)).\n\t\t\tLeftJoin(cte, Eq(cte.Field(\"actor_id\"), a.ACTOR_ID)).\n\t\t\tCrossJoin(a).\n\t\t\tCustomJoin(\",\", a).\n\t\t\tJoinUsing(a, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelectOne()\n\t\ttt.wantQuery = \"WITH cte AS (SELECT * FROM actor AS a)\" +\n\t\t\t\" SELECT 1\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" JOIN (SELECT * FROM actor AS a) AS subquery ON subquery.actor_id = a.actor_id\" +\n\t\t\t\" LEFT JOIN cte ON cte.actor_id = a.actor_id\" +\n\t\t\t\" CROSS JOIN actor AS a\" +\n\t\t\t\" , actor AS a\" +\n\t\t\t\" JOIN actor AS a USING (first_name, last_name)\"\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"all\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLite.\n\t\t\tSelectDistinct(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelectDistinct(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tFrom(a).\n\t\t\tWhere(a.ACTOR_ID.GtInt(5)).\n\t\t\tGroupBy(a.FIRST_NAME).\n\t\t\tHaving(a.FIRST_NAME.IsNotNull()).\n\t\t\tOrderBy(a.LAST_NAME).\n\t\t\tLimit(10).\n\t\t\tOffset(20)\n\t\ttt.wantQuery = \"SELECT DISTINCT a.actor_id, a.first_name, a.last_name\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" WHERE a.actor_id > $1\" +\n\t\t\t\" GROUP BY a.first_name\" +\n\t\t\t\" HAVING a.first_name IS NOT NULL\" +\n\t\t\t\" ORDER BY a.last_name\" +\n\t\t\t\" LIMIT $2\" +\n\t\t\t\" OFFSET $3\"\n\t\ttt.wantArgs = []any{5, 10, 20}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestPostgresSelectQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := Postgres.SelectOne().From(a).SetDialect(\"lorem ipsum\").As(\"q1\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif diff := testutil.Diff(q1.GetAlias(), \"q1\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.ACTOR_ID})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.SelectFields = q1.SelectFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.ACTOR_ID})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"subquery, cte and joins\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\tsubquery := Postgres.Select(Expr(\"*\")).From(a).As(\"subquery\")\n\t\tcte := NewCTE(\"cte\", nil, Postgres.From(a).Select(Expr(\"*\")))\n\t\ttt.item = Postgres.\n\t\t\tWith(cte).\n\t\t\tFrom(a).From(a).\n\t\t\tJoin(subquery, Eq(subquery.Field(\"actor_id\"), a.ACTOR_ID)).\n\t\t\tLeftJoin(cte, Eq(cte.Field(\"actor_id\"), a.ACTOR_ID)).\n\t\t\tFullJoin(a, Expr(\"1 = 1\")).\n\t\t\tCrossJoin(a).\n\t\t\tCustomJoin(\",\", a).\n\t\t\tJoinUsing(a, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelectOne()\n\t\ttt.wantQuery = \"WITH cte AS (SELECT * FROM actor AS a)\" +\n\t\t\t\" SELECT 1\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" JOIN (SELECT * FROM actor AS a) AS subquery ON subquery.actor_id = a.actor_id\" +\n\t\t\t\" LEFT JOIN cte ON cte.actor_id = a.actor_id\" +\n\t\t\t\" FULL JOIN actor AS a ON 1 = 1\" +\n\t\t\t\" CROSS JOIN actor AS a\" +\n\t\t\t\" , actor AS a\" +\n\t\t\t\" JOIN actor AS a USING (first_name, last_name)\"\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"all\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tSelectDistinct(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelectDistinct(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tFrom(a).\n\t\t\tWhere(a.ACTOR_ID.GtInt(5)).\n\t\t\tGroupBy(a.FIRST_NAME).\n\t\t\tHaving(a.FIRST_NAME.IsNotNull()).\n\t\t\tOrderBy(a.LAST_NAME).\n\t\t\tLimit(10).\n\t\t\tOffset(20)\n\t\ttt.wantQuery = \"SELECT DISTINCT a.actor_id, a.first_name, a.last_name\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" WHERE a.actor_id > $1\" +\n\t\t\t\" GROUP BY a.first_name\" +\n\t\t\t\" HAVING a.first_name IS NOT NULL\" +\n\t\t\t\" ORDER BY a.last_name\" +\n\t\t\t\" LIMIT $2\" +\n\t\t\t\" OFFSET $3\"\n\t\ttt.wantArgs = []any{5, 10, 20}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"DistinctOn\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tSelect(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tDistinctOn(a.FIRST_NAME, a.LAST_NAME).\n\t\t\tFrom(a)\n\t\ttt.wantQuery = \"SELECT DISTINCT ON (a.first_name, a.last_name)\" +\n\t\t\t\" a.actor_id, a.first_name, a.last_name\" +\n\t\t\t\" FROM actor AS a\"\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"FetchNext, WithTies, LockRows\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tSelect(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tFrom(a).\n\t\t\tOrderBy(a.ACTOR_ID).\n\t\t\tOffset(10).\n\t\t\tFetchNext(20).WithTies().\n\t\t\tLockRows(\"FOR UPDATE\")\n\t\ttt.wantQuery = \"SELECT a.actor_id, a.first_name, a.last_name\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" ORDER BY a.actor_id\" +\n\t\t\t\" OFFSET $1\" +\n\t\t\t\" FETCH NEXT $2 ROWS WITH TIES\" +\n\t\t\t\" FOR UPDATE\"\n\t\ttt.wantArgs = []any{10, 20}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestMySQLSelectQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := MySQL.SelectOne().From(a).SetDialect(\"lorem ipsum\").As(\"q1\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif diff := testutil.Diff(q1.GetAlias(), \"q1\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.ACTOR_ID})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.SelectFields = q1.SelectFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.ACTOR_ID})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"subquery, cte and joins\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\tsubquery := MySQL.Select(Expr(\"*\")).From(a).As(\"subquery\")\n\t\tcte := NewCTE(\"cte\", nil, MySQL.From(a).Select(Expr(\"*\")))\n\t\ttt.item = MySQL.\n\t\t\tWith(cte).\n\t\t\tFrom(a).From(a).\n\t\t\tJoin(subquery, Eq(subquery.Field(\"actor_id\"), a.ACTOR_ID)).\n\t\t\tLeftJoin(cte, Eq(cte.Field(\"actor_id\"), a.ACTOR_ID)).\n\t\t\tFullJoin(a, Expr(\"1 = 1\")).\n\t\t\tCrossJoin(a).\n\t\t\tCustomJoin(\",\", a).\n\t\t\tJoinUsing(a, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelectOne()\n\t\ttt.wantQuery = \"WITH cte AS (SELECT * FROM actor AS a)\" +\n\t\t\t\" SELECT 1\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" JOIN (SELECT * FROM actor AS a) AS subquery ON subquery.actor_id = a.actor_id\" +\n\t\t\t\" LEFT JOIN cte ON cte.actor_id = a.actor_id\" +\n\t\t\t\" FULL JOIN actor AS a ON 1 = 1\" +\n\t\t\t\" CROSS JOIN actor AS a\" +\n\t\t\t\" , actor AS a\" +\n\t\t\t\" JOIN actor AS a USING (first_name, last_name)\"\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"all\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tSelectDistinct(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelectDistinct(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tFrom(a).\n\t\t\tWhere(a.ACTOR_ID.GtInt(5)).\n\t\t\tGroupBy(a.FIRST_NAME).\n\t\t\tHaving(a.FIRST_NAME.IsNotNull()).\n\t\t\tOrderBy(a.LAST_NAME).\n\t\t\tLimit(10).\n\t\t\tOffset(20)\n\t\ttt.wantQuery = \"SELECT DISTINCT a.actor_id, a.first_name, a.last_name\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" WHERE a.actor_id > ?\" +\n\t\t\t\" GROUP BY a.first_name\" +\n\t\t\t\" HAVING a.first_name IS NOT NULL\" +\n\t\t\t\" ORDER BY a.last_name\" +\n\t\t\t\" LIMIT ?\" +\n\t\t\t\" OFFSET ?\"\n\t\ttt.wantArgs = []any{5, 10, 20}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"LockRows\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tSelect(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tFrom(a).\n\t\t\tOrderBy(a.ACTOR_ID).\n\t\t\tOffset(10).\n\t\t\tLockRows(\"FOR UPDATE\")\n\t\ttt.wantQuery = \"SELECT a.actor_id, a.first_name, a.last_name\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" ORDER BY a.actor_id\" +\n\t\t\t\" OFFSET ?\" +\n\t\t\t\" FOR UPDATE\"\n\t\ttt.wantArgs = []any{10}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestSQLServerSelectQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := SQLServer.SelectOne().From(a).SetDialect(\"lorem ipsum\").As(\"q1\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif diff := testutil.Diff(q1.GetAlias(), \"q1\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.ACTOR_ID})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.SelectFields = q1.SelectFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.ACTOR_ID})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"subquery, cte and joins\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\tsubquery := SQLServer.Select(Expr(\"*\")).From(a).As(\"subquery\")\n\t\tcte := NewCTE(\"cte\", nil, SQLServer.From(a).Select(Expr(\"*\")))\n\t\ttt.item = SQLServer.\n\t\t\tWith(cte).\n\t\t\tFrom(a).From(a).\n\t\t\tJoin(subquery, Eq(subquery.Field(\"actor_id\"), a.ACTOR_ID)).\n\t\t\tLeftJoin(cte, Eq(cte.Field(\"actor_id\"), a.ACTOR_ID)).\n\t\t\tFullJoin(a, Expr(\"1 = 1\")).\n\t\t\tCrossJoin(a).\n\t\t\tCustomJoin(\",\", a).\n\t\t\tSelectOne()\n\t\ttt.wantQuery = \"WITH cte AS (SELECT * FROM actor AS a)\" +\n\t\t\t\" SELECT 1\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" JOIN (SELECT * FROM actor AS a) AS subquery ON subquery.actor_id = a.actor_id\" +\n\t\t\t\" LEFT JOIN cte ON cte.actor_id = a.actor_id\" +\n\t\t\t\" FULL JOIN actor AS a ON 1 = 1\" +\n\t\t\t\" CROSS JOIN actor AS a\" +\n\t\t\t\" , actor AS a\"\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"all\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLServer.\n\t\t\tSelectDistinct(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSelectDistinct(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tFrom(a).\n\t\t\tWhere(a.ACTOR_ID.GtInt(5)).\n\t\t\tGroupBy(a.FIRST_NAME).\n\t\t\tHaving(a.FIRST_NAME.IsNotNull()).\n\t\t\tOrderBy(a.LAST_NAME).\n\t\t\tOffset(10).\n\t\t\tFetchNext(20)\n\t\ttt.wantQuery = \"SELECT DISTINCT a.actor_id, a.first_name, a.last_name\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" WHERE a.actor_id > @p1\" +\n\t\t\t\" GROUP BY a.first_name\" +\n\t\t\t\" HAVING a.first_name IS NOT NULL\" +\n\t\t\t\" ORDER BY a.last_name\" +\n\t\t\t\" OFFSET @p2 ROWS\" +\n\t\t\t\" FETCH NEXT @p3 ROWS ONLY\"\n\t\ttt.wantArgs = []any{5, 10, 20}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"TopPercent\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLServer.\n\t\t\tSelect(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tTopPercent(5).\n\t\t\tFrom(a).\n\t\t\tOrderBy(a.ACTOR_ID)\n\t\ttt.wantQuery = \"SELECT TOP (@p1) PERCENT a.actor_id, a.first_name, a.last_name\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" ORDER BY a.actor_id\"\n\t\ttt.wantArgs = []any{5}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"Top, WithTies\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLServer.\n\t\t\tSelect(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tTop(10).WithTies().\n\t\t\tFrom(Expr(\"{} AS a WITH (UPDLOCK, ROWLOCK)\", a)).\n\t\t\tOrderBy(a.ACTOR_ID)\n\t\ttt.wantQuery = \"SELECT TOP (@p1) WITH TIES a.actor_id, a.first_name, a.last_name\" +\n\t\t\t\" FROM actor AS a WITH (UPDLOCK, ROWLOCK)\" +\n\t\t\t\" ORDER BY a.actor_id\"\n\t\ttt.wantArgs = []any{10}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestSelectQuery(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := SelectQuery{FromTable: Expr(\"tbl\"), Dialect: \"lorem ipsum\", Alias: \"q1\"}\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tif diff := testutil.Diff(q1.GetAlias(), \"q1\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{Expr(\"f1\")})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"not ok\")\n\t\t}\n\t})\n\n\tt.Run(\"PolicyTable\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SelectQuery{\n\t\t\tSelectFields:   Fields{Expr(\"1\")},\n\t\t\tFromTable:      policyTableStub{policy: And(Expr(\"1 = 1\"), Expr(\"2 = 2\"))},\n\t\t\tWherePredicate: Expr(\"3 = 3\"),\n\t\t}\n\t\ttt.wantQuery = \"SELECT 1 FROM policy_table_stub WHERE (1 = 1 AND 2 = 2) AND 3 = 3\"\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"Where Having Window\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\tw1 := NamedWindow{Name: \"w1\", Definition: PartitionBy(Expr(\"f1\"))}\n\t\tw2 := NamedWindow{Name: \"w2\", Definition: OrderBy(Expr(\"f2\"))}\n\t\tw3 := NamedWindow{Name: \"w3\", Definition: OrderBy(Expr(\"f3\")).Frame(\"ROWS UNBOUNDED PRECEDING\")}\n\t\ttt.item = SelectQuery{\n\t\t\tSelectFields:    Fields{CountStarOver(w1), CountStarOver(w2), CountStarOver(w3)},\n\t\t\tFromTable:       Expr(\"tbl\"),\n\t\t\tWherePredicate:  Expr(\"1 = 1\"),\n\t\t\tGroupByFields:   Fields{Expr(\"f2\")},\n\t\t\tHavingPredicate: Expr(\"2 = 2\"),\n\t\t\tNamedWindows:    NamedWindows{w1, w2, w3},\n\t\t}\n\t\ttt.wantQuery = \"SELECT COUNT(*) OVER w1, COUNT(*) OVER w2, COUNT(*) OVER w3\" +\n\t\t\t\" FROM tbl\" +\n\t\t\t\" WHERE 1 = 1\" +\n\t\t\t\" GROUP BY f2\" +\n\t\t\t\" HAVING 2 = 2\" +\n\t\t\t\" WINDOW w1 AS (PARTITION BY f1)\" +\n\t\t\t\", w2 AS (ORDER BY f2)\" +\n\t\t\t\", w3 AS (ORDER BY f3 ROWS UNBOUNDED PRECEDING)\"\n\t\ttt.assert(t)\n\t})\n\n\tnotOKTests := []TestTable{{\n\t\tdescription: \"no fields provided not allowed\",\n\t\titem:        SelectQuery{},\n\t}, {\n\t\tdescription: \"dialect does not support TOP\",\n\t\titem: SelectQuery{\n\t\t\tDialect:      DialectSQLite,\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tLimitTop:     5,\n\t\t},\n\t}, {\n\t\tdescription: \"sqlserver does not allow TOP without ORDER BY\",\n\t\titem: SelectQuery{\n\t\t\tDialect:      DialectSQLServer,\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tLimitTop:     5,\n\t\t},\n\t}, {\n\t\tdescription: \"dialect does not support DISTINCT ON\",\n\t\titem: SelectQuery{\n\t\t\tDialect:          DialectSQLite,\n\t\t\tSelectFields:     Fields{Expr(\"f1\")},\n\t\t\tDistinctOnFields: Fields{Expr(\"f2\")},\n\t\t},\n\t}, {\n\t\tdescription: \"postgres does not allow both DISTINCT and DISTINCT ON\",\n\t\titem: SelectQuery{\n\t\t\tDialect:          DialectPostgres,\n\t\t\tSelectFields:     Fields{Expr(\"f1\")},\n\t\t\tDistinct:         true,\n\t\t\tDistinctOnFields: Fields{Expr(\"f2\")},\n\t\t},\n\t}, {\n\t\tdescription: \"postgres does not allow subquery no alias\",\n\t\titem: SelectQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tFromTable:    SelectQuery{SelectFields: Fields{Expr(\"f1\")}},\n\t\t},\n\t}, {\n\t\tdescription: \"JOIN without FROM not allowed\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tJoinTables: []JoinTable{\n\t\t\t\tJoin(Expr(\"tbl\"), Expr(\"1 = 1\")),\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"sqlserver does not support LIMIT\",\n\t\titem: SelectQuery{\n\t\t\tDialect:      DialectSQLServer,\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t\tLimitRows:    5,\n\t\t},\n\t}, {\n\t\tdescription: \"sqlserver does not allow OFFSET without ORDER BY\",\n\t\titem: SelectQuery{\n\t\t\tDialect:      DialectSQLServer,\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t\tOffsetRows:   5,\n\t\t},\n\t}, {\n\t\tdescription: \"sqlserver does not support OFFSET with TOP\",\n\t\titem: SelectQuery{\n\t\t\tDialect:       DialectSQLServer,\n\t\t\tSelectFields:  Fields{Expr(\"f1\")},\n\t\t\tLimitTop:      5,\n\t\t\tFromTable:     Expr(\"tbl\"),\n\t\t\tOrderByFields: Fields{Expr(\"f1\")},\n\t\t\tOffsetRows:    10,\n\t\t},\n\t}, {\n\t\tdescription: \"postgres does not allow FETCH NEXT with LIMIT\",\n\t\titem: SelectQuery{\n\t\t\tDialect:       DialectPostgres,\n\t\t\tSelectFields:  Fields{Expr(\"f1\")},\n\t\t\tFromTable:     Expr(\"tbl\"),\n\t\t\tOrderByFields: Fields{Expr(\"f1\")},\n\t\t\tLimitRows:     5,\n\t\t\tOffsetRows:    10,\n\t\t\tFetchNextRows: 20,\n\t\t},\n\t}, {\n\t\tdescription: \"sqlserver does not allow FETCH NEXT with TOP\",\n\t\titem: SelectQuery{\n\t\t\tDialect:       DialectSQLServer,\n\t\t\tSelectFields:  Fields{Expr(\"f1\")},\n\t\t\tLimitTop:      5,\n\t\t\tFromTable:     Expr(\"tbl\"),\n\t\t\tOrderByFields: Fields{Expr(\"f1\")},\n\t\t\tFetchNextRows: 10,\n\t\t},\n\t}, {\n\t\tdescription: \"dialect does not support FETCH NEXT\",\n\t\titem: SelectQuery{\n\t\t\tDialect:       DialectSQLite,\n\t\t\tSelectFields:  Fields{Expr(\"f1\")},\n\t\t\tFromTable:     Expr(\"tbl\"),\n\t\t\tOrderByFields: Fields{Expr(\"f1\")},\n\t\t\tFetchNextRows: 20,\n\t\t},\n\t}, {\n\t\tdescription: \"sqlserver does not support FETCH NEXT with WITH TIES\",\n\t\titem: SelectQuery{\n\t\t\tDialect:       DialectSQLServer,\n\t\t\tSelectFields:  Fields{Expr(\"f1\")},\n\t\t\tFromTable:     Expr(\"tbl\"),\n\t\t\tOrderByFields: Fields{Expr(\"f1\")},\n\t\t\tFetchNextRows: 20,\n\t\t\tFetchWithTies: true,\n\t\t},\n\t}, {\n\t\tdescription: \"postgres does not allow WITH TIES without ORDER BY\",\n\t\titem: SelectQuery{\n\t\t\tDialect:       DialectPostgres,\n\t\t\tSelectFields:  Fields{Expr(\"f1\")},\n\t\t\tFromTable:     Expr(\"tbl\"),\n\t\t\tFetchNextRows: 20,\n\t\t\tFetchWithTies: true,\n\t\t},\n\t}}\n\n\tfor _, tt := range notOKTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertNotOK(t)\n\t\t})\n\t}\n\n\terrTests := []TestTable{{\n\t\tdescription: \"FromTable Policy err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tFromTable:    policyTableStub{err: ErrFaultySQL},\n\t\t},\n\t}, {\n\t\tdescription: \"JoinTables Policy err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t\tJoinTables: []JoinTable{\n\t\t\t\tJoin(policyTableStub{err: ErrFaultySQL}, Expr(\"1 = 1\")),\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"CTEs err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tCTEs: []CTE{\n\t\t\t\tNewCTE(\"cte\", nil, Queryf(\"{}\", FaultySQL{})),\n\t\t\t},\n\t\t\tFromTable: Expr(\"tbl\"),\n\t\t},\n\t}, {\n\t\tdescription: \"sqlserver LimitTop err\",\n\t\titem: SelectQuery{\n\t\t\tDialect:       DialectSQLServer,\n\t\t\tSelectFields:  Fields{Expr(\"f1\")},\n\t\t\tLimitTop:      FaultySQL{},\n\t\t\tFromTable:     Expr(\"tbl\"),\n\t\t\tOrderByFields: Fields{Expr(\"f1\")},\n\t\t},\n\t}, {\n\t\tdescription: \"postgres DistinctOnFields err\",\n\t\titem: SelectQuery{\n\t\t\tDialect:          DialectPostgres,\n\t\t\tSelectFields:     Fields{Expr(\"f1\")},\n\t\t\tDistinctOnFields: Fields{FaultySQL{}},\n\t\t\tFromTable:        Expr(\"tbl\"),\n\t\t},\n\t}, {\n\t\tdescription: \"SelectFields err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields: Fields{FaultySQL{}},\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t},\n\t}, {\n\t\tdescription: \"FromTable err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tFromTable:    FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"JoinTables err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t\tJoinTables: []JoinTable{\n\t\t\t\tJoin(Expr(\"tbl\"), FaultySQL{}),\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"WherePredicate VariadicPredicate err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields:   Fields{Expr(\"f1\")},\n\t\t\tFromTable:      Expr(\"tbl\"),\n\t\t\tWherePredicate: And(FaultySQL{}),\n\t\t},\n\t}, {\n\t\tdescription: \"WherePredicate err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields:   Fields{Expr(\"f1\")},\n\t\t\tFromTable:      Expr(\"tbl\"),\n\t\t\tWherePredicate: FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"GroupBy err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields:  Fields{Expr(\"f1\")},\n\t\t\tFromTable:     Expr(\"tbl\"),\n\t\t\tGroupByFields: Fields{FaultySQL{}},\n\t\t},\n\t}, {\n\t\tdescription: \"HavingPredicate VariadicPredicate err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields:    Fields{Expr(\"f1\")},\n\t\t\tFromTable:       Expr(\"tbl\"),\n\t\t\tGroupByFields:   Fields{Expr(\"f1\")},\n\t\t\tHavingPredicate: And(FaultySQL{}),\n\t\t},\n\t}, {\n\t\tdescription: \"HavingPredicate err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields:    Fields{Expr(\"f1\")},\n\t\t\tFromTable:       Expr(\"tbl\"),\n\t\t\tGroupByFields:   Fields{Expr(\"f1\")},\n\t\t\tHavingPredicate: FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"NamedWindows err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t\tNamedWindows: NamedWindows{{\n\t\t\t\tName:       \"w\",\n\t\t\t\tDefinition: OrderBy(FaultySQL{}),\n\t\t\t}},\n\t\t},\n\t}, {\n\t\tdescription: \"OrderByFields err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields:  Fields{Expr(\"f1\")},\n\t\t\tFromTable:     Expr(\"tbl\"),\n\t\t\tOrderByFields: Fields{FaultySQL{}},\n\t\t},\n\t}, {\n\t\tdescription: \"LimitRows err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t\tLimitRows:    FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"OffsetRows err\",\n\t\titem: SelectQuery{\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t\tOffsetRows:   FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"FetchNext err\",\n\t\titem: SelectQuery{\n\t\t\tDialect:       DialectPostgres,\n\t\t\tSelectFields:  Fields{Expr(\"f1\")},\n\t\t\tFromTable:     Expr(\"tbl\"),\n\t\t\tFetchNextRows: FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"LockClause err\",\n\t\titem: SelectQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tSelectFields: Fields{Expr(\"f1\")},\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t\tLockClause:   \"FOR UPDATE OF {}\",\n\t\t\tLockValues:   []any{FaultySQL{}},\n\t\t},\n\t}}\n\n\tfor _, tt := range errTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertErr(t, ErrFaultySQL)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "sq.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/bokwoon95/sq/internal/googleuuid\"\n\t\"github.com/bokwoon95/sq/internal/pqarray\"\n)\n\nvar bufpool = &sync.Pool{\n\tNew: func() any { return &bytes.Buffer{} },\n}\n\n// Dialects supported.\nconst (\n\tDialectSQLite    = \"sqlite\"\n\tDialectPostgres  = \"postgres\"\n\tDialectMySQL     = \"mysql\"\n\tDialectSQLServer = \"sqlserver\"\n)\n\n// SQLWriter is anything that can be converted to SQL.\ntype SQLWriter interface {\n\t// WriteSQL writes the SQL representation of the SQLWriter into the query\n\t// string (*bytes.Buffer) and args slice (*[]any).\n\t//\n\t// The params map is used to hold the mappings between named parameters in\n\t// the query to the corresponding index in the args slice and is used for\n\t// rebinding args by their parameter name. The params map may be nil, check\n\t// first before writing to it.\n\tWriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error\n}\n\n// DB is a database/sql abstraction that can query the database. *sql.Conn,\n// *sql.DB and *sql.Tx all implement DB.\ntype DB interface {\n\tQueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)\n\tExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)\n\tPrepareContext(ctx context.Context, query string) (*sql.Stmt, error)\n}\n\n// Result is the result of an Exec command.\ntype Result struct {\n\tLastInsertId int64\n\tRowsAffected int64\n}\n\n// Query is either SELECT, INSERT, UPDATE or DELETE.\ntype Query interface {\n\tSQLWriter\n\t// SetFetchableFields should return a query with its fetchable fields set\n\t// to the given fields. If not applicable, it should return false as the\n\t// second return value.\n\tSetFetchableFields([]Field) (query Query, ok bool)\n\tGetDialect() string\n}\n\n// Table is anything you can Select from or Join.\ntype Table interface {\n\tSQLWriter\n\tIsTable()\n}\n\n// PolicyTable is a table that produces a policy (i.e. a predicate) to be\n// enforced whenever it is invoked in a query. This is equivalent to Postgres'\n// Row Level Security (RLS) feature but works application-side. Only SELECT,\n// UPDATE and DELETE queries are affected.\ntype PolicyTable interface {\n\tTable\n\tPolicy(ctx context.Context, dialect string) (Predicate, error)\n}\n\n// Window is a window used in SQL window functions.\ntype Window interface {\n\tSQLWriter\n\tIsWindow()\n}\n\n// Field is either a table column or some SQL expression.\ntype Field interface {\n\tSQLWriter\n\tIsField()\n}\n\n// Predicate is an SQL expression that evaluates to true or false.\ntype Predicate interface {\n\tBoolean\n}\n\n// Assignment is an SQL assignment 'field = value'.\ntype Assignment interface {\n\tSQLWriter\n\tIsAssignment()\n}\n\n// Any is a catch-all interface that covers every field type.\ntype Any interface {\n\tArray\n\tBinary\n\tBoolean\n\tEnum\n\tJSON\n\tNumber\n\tString\n\tTime\n\tUUID\n}\n\n// Enumeration represents a Go enum.\ntype Enumeration interface {\n\t// Enumerate returns the names of all valid enum values.\n\t//\n\t// If the enum is backed by a string, each string in the slice is the\n\t// corresponding enum's string value.\n\t//\n\t// If the enum is backed by an int, each int index in the slice is the\n\t// corresponding enum's int value and the string is the enum's name. Enums\n\t// with empty string names are considered invalid, unless it is the very\n\t// first enum (at index 0).\n\tEnumerate() []string\n}\n\n// Array is a Field of array type.\ntype Array interface {\n\tField\n\tIsArray()\n}\n\n// Binary is a Field of binary type.\ntype Binary interface {\n\tField\n\tIsBinary()\n}\n\n// Boolean is a Field of boolean type.\ntype Boolean interface {\n\tField\n\tIsBoolean()\n}\n\n// Enum is a Field of enum type.\ntype Enum interface {\n\tField\n\tIsEnum()\n}\n\n// JSON is a Field of json type.\ntype JSON interface {\n\tField\n\tIsJSON()\n}\n\n// Number is a Field of numeric type.\ntype Number interface {\n\tField\n\tIsNumber()\n}\n\n// String is a Field of string type.\ntype String interface {\n\tField\n\tIsString()\n}\n\n// Time is a Field of time type.\ntype Time interface {\n\tField\n\tIsTime()\n}\n\n// UUID is a Field of uuid type.\ntype UUID interface {\n\tField\n\tIsUUID()\n}\n\n// DialectValuer is any type that will yield a different driver.Valuer\n// depending on the SQL dialect.\ntype DialectValuer interface {\n\tDialectValuer(dialect string) (driver.Valuer, error)\n}\n\n// TableStruct is meant to be embedded in table structs to make them implement\n// the Table interface.\ntype TableStruct struct {\n\tschema string\n\tname   string\n\talias  string\n}\n\n// ViewStruct is just an alias for TableStruct.\ntype ViewStruct = TableStruct\n\nvar _ Table = (*TableStruct)(nil)\n\n// NewTableStruct creates a new TableStruct.\nfunc NewTableStruct(schema, name, alias string) TableStruct {\n\treturn TableStruct{schema: schema, name: name, alias: alias}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (ts TableStruct) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tif ts.schema != \"\" {\n\t\tbuf.WriteString(QuoteIdentifier(dialect, ts.schema) + \".\")\n\t}\n\tbuf.WriteString(QuoteIdentifier(dialect, ts.name))\n\treturn nil\n}\n\n// GetAlias returns the alias of the TableStruct.\nfunc (ts TableStruct) GetAlias() string { return ts.alias }\n\n// IsTable implements the Table interface.\nfunc (ts TableStruct) IsTable() {}\n\nfunc withPrefix(w SQLWriter, prefix string) SQLWriter {\n\tif field, ok := w.(interface {\n\t\tSQLWriter\n\t\tWithPrefix(string) Field\n\t}); ok {\n\t\treturn field.WithPrefix(prefix)\n\t}\n\treturn w\n}\n\nfunc getAlias(w SQLWriter) string {\n\tif w, ok := w.(interface{ GetAlias() string }); ok {\n\t\treturn w.GetAlias()\n\t}\n\treturn \"\"\n}\n\nfunc toString(dialect string, w SQLWriter) string {\n\tbuf := bufpool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer bufpool.Put(buf)\n\tvar args []any\n\t_ = w.WriteSQL(context.Background(), dialect, buf, &args, nil)\n\treturn buf.String()\n}\n\nfunc writeFieldsWithPrefix(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, fields []Field, prefix string, includeAlias bool) error {\n\tvar err error\n\tvar alias string\n\tfor i, field := range fields {\n\t\tif field == nil {\n\t\t\treturn fmt.Errorf(\"field #%d is nil\", i+1)\n\t\t}\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\terr = withPrefix(field, prefix).WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"field #%d: %w\", i+1, err)\n\t\t}\n\t\tif includeAlias {\n\t\t\tif alias = getAlias(field); alias != \"\" {\n\t\t\t\tbuf.WriteString(\" AS \" + QuoteIdentifier(dialect, alias))\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc writeFields(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int, fields []Field, includeAlias bool) error {\n\tvar err error\n\tvar alias string\n\tfor i, field := range fields {\n\t\tif field == nil {\n\t\t\treturn fmt.Errorf(\"field #%d is nil\", i+1)\n\t\t}\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\t_, isQuery := field.(Query)\n\t\tif isQuery {\n\t\t\tbuf.WriteString(\"(\")\n\t\t}\n\t\terr = field.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"field #%d: %w\", i+1, err)\n\t\t}\n\t\tif isQuery {\n\t\t\tbuf.WriteString(\")\")\n\t\t}\n\t\tif includeAlias {\n\t\t\tif alias = getAlias(field); alias != \"\" {\n\t\t\t\tbuf.WriteString(\" AS \" + QuoteIdentifier(dialect, alias))\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// mapperFunctionPanicked recovers from any panics.\n//\n// The function is called as such so that it shows up as\n// \"sq.mapperFunctionPanicked\" in panic stack trace, giving the user a\n// descriptive clue of what went wrong (i.e. their mapper function panicked).\nfunc mapperFunctionPanicked(err *error) {\n\tif r := recover(); r != nil {\n\t\tswitch r := r.(type) {\n\t\tcase error:\n\t\t\t*err = r\n\t\tdefault:\n\t\t\t*err = fmt.Errorf(fmt.Sprint(r))\n\t\t}\n\t}\n}\n\n// ArrayValue takes in a []string, []int, []int64, []int32, []float64,\n// []float32 or []bool and returns a driver.Valuer for that type. For Postgres,\n// it serializes into a Postgres array. Otherwise, it serializes into a JSON\n// array.\nfunc ArrayValue(value any) driver.Valuer {\n\treturn &arrayValue{value: value}\n}\n\ntype arrayValue struct {\n\tdialect string\n\tvalue   any\n}\n\n// Value implements the driver.Valuer interface.\nfunc (v *arrayValue) Value() (driver.Value, error) {\n\tswitch v.value.(type) {\n\tcase []string, []int, []int64, []int32, []float64, []float32, []bool:\n\t\tbreak\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"value %#v is not a []string, []int, []int32, []float64, []float32 or []bool\", v.value)\n\t}\n\tif v.dialect != DialectPostgres {\n\t\tvar b strings.Builder\n\t\terr := json.NewEncoder(&b).Encode(v.value)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn strings.TrimSpace(b.String()), nil\n\t}\n\tif ints, ok := v.value.([]int); ok {\n\t\tbigints := make([]int64, len(ints))\n\t\tfor i, num := range ints {\n\t\t\tbigints[i] = int64(num)\n\t\t}\n\t\tv.value = bigints\n\t}\n\treturn pqarray.Array(v.value).Value()\n}\n\n// DialectValuer implements the DialectValuer interface.\nfunc (v *arrayValue) DialectValuer(dialect string) (driver.Valuer, error) {\n\tv.dialect = dialect\n\treturn v, nil\n}\n\n// EnumValue takes in an Enumeration and returns a driver.Valuer which\n// serializes the enum into a string and additionally checks if the enum is\n// valid.\nfunc EnumValue(value Enumeration) driver.Valuer {\n\treturn &enumValue{value: value}\n}\n\ntype enumValue struct {\n\tvalue Enumeration\n}\n\n// Value implements the driver.Valuer interface.\nfunc (v *enumValue) Value() (driver.Value, error) {\n\tvalue := reflect.ValueOf(v.value)\n\tnames := v.value.Enumerate()\n\tswitch value.Kind() {\n\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\ti := int(value.Int())\n\t\tif i < 0 || i >= len(names) {\n\t\t\treturn nil, fmt.Errorf(\"%d is not a valid %T\", i, v.value)\n\t\t}\n\t\tname := names[i]\n\t\tif name == \"\" && i != 0 {\n\t\t\treturn nil, fmt.Errorf(\"%d is not a valid %T\", i, v.value)\n\t\t}\n\t\treturn name, nil\n\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\ti := int(value.Uint())\n\t\tif i < 0 || i >= len(names) {\n\t\t\treturn nil, fmt.Errorf(\"%d is not a valid %T\", i, v.value)\n\t\t}\n\t\tname := names[i]\n\t\tif name == \"\" && i != 0 {\n\t\t\treturn nil, fmt.Errorf(\"%d is not a valid %T\", i, v.value)\n\t\t}\n\t\treturn name, nil\n\tcase reflect.String:\n\t\ttyp := value.Type()\n\t\tname := value.String()\n\t\tif getEnumIndex(name, names, typ) < 0 {\n\t\t\treturn nil, fmt.Errorf(\"%q is not a valid %T\", name, v.value)\n\t\t}\n\t\treturn name, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"underlying type of %[1]v is neither an integer nor string (%[1]T)\", v.value)\n\t}\n}\n\nvar (\n\tenumIndexMu sync.RWMutex\n\tenumIndex   = make(map[reflect.Type]map[string]int)\n)\n\n// getEnumIndex returns the index of the enum within the names slice.\nfunc getEnumIndex(name string, names []string, typ reflect.Type) int {\n\tif len(names) <= 4 {\n\t\tfor idx := range names {\n\t\t\tif names[idx] == name {\n\t\t\t\treturn idx\n\t\t\t}\n\t\t}\n\t\treturn -1\n\t}\n\tvar nameIndex map[string]int\n\tenumIndexMu.RLock()\n\tnameIndex = enumIndex[typ]\n\tenumIndexMu.RUnlock()\n\tif nameIndex != nil {\n\t\tidx, ok := nameIndex[name]\n\t\tif !ok {\n\t\t\treturn -1\n\t\t}\n\t\treturn idx\n\t}\n\tidx := -1\n\tnameIndex = make(map[string]int)\n\tfor i := range names {\n\t\tif names[i] == name {\n\t\t\tidx = i\n\t\t}\n\t\tnameIndex[names[i]] = i\n\t}\n\tenumIndexMu.Lock()\n\tenumIndex[typ] = nameIndex\n\tenumIndexMu.Unlock()\n\treturn idx\n}\n\n// JSONValue takes in an interface{} and returns a driver.Valuer which runs the\n// value through json.Marshal before submitting it to the database.\nfunc JSONValue(value any) driver.Valuer {\n\treturn &jsonValue{value: value}\n}\n\ntype jsonValue struct {\n\tvalue any\n}\n\n// Value implements the driver.Valuer interface.\nfunc (v *jsonValue) Value() (driver.Value, error) {\n\tvar b strings.Builder\n\terr := json.NewEncoder(&b).Encode(v.value)\n\treturn strings.TrimSpace(b.String()), err\n}\n\n// UUIDValue takes in a type whose underlying type must be a [16]byte and\n// returns a driver.Valuer.\nfunc UUIDValue(value any) driver.Valuer {\n\treturn &uuidValue{value: value}\n}\n\ntype uuidValue struct {\n\tdialect string\n\tvalue   any\n}\n\n// Value implements the driver.Valuer interface.\nfunc (v *uuidValue) Value() (driver.Value, error) {\n\tif v.value == nil {\n\t\treturn nil, nil\n\t}\n\tuuid, ok := v.value.([16]byte)\n\tif !ok {\n\t\tvalue := reflect.ValueOf(v.value)\n\t\ttyp := value.Type()\n\t\tif value.Kind() != reflect.Array || value.Len() != 16 || typ.Elem().Kind() != reflect.Uint8 {\n\t\t\treturn nil, fmt.Errorf(\"%[1]v %[1]T is not [16]byte\", v.value)\n\t\t}\n\t\tfor i := 0; i < value.Len(); i++ {\n\t\t\tuuid[i] = value.Index(i).Interface().(byte)\n\t\t}\n\t}\n\tif v.dialect != DialectPostgres {\n\t\treturn uuid[:], nil\n\t}\n\tvar buf [36]byte\n\tgoogleuuid.EncodeHex(buf[:], uuid)\n\treturn string(buf[:]), nil\n}\n\n// DialectValuer implements the DialectValuer interface.\nfunc (v *uuidValue) DialectValuer(dialect string) (driver.Valuer, error) {\n\tv.dialect = dialect\n\treturn v, nil\n}\n\nfunc preprocessValue(dialect string, value any) (any, error) {\n\tif dialectValuer, ok := value.(DialectValuer); ok {\n\t\tdriverValuer, err := dialectValuer.DialectValuer(dialect)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"calling DialectValuer on %#v: %w\", dialectValuer, err)\n\t\t}\n\t\tvalue = driverValuer\n\t}\n\tswitch value := value.(type) {\n\tcase nil:\n\t\treturn nil, nil\n\tcase Enumeration:\n\t\tdriverValue, err := (&enumValue{value: value}).Value()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"converting %#v to string: %w\", value, err)\n\t\t}\n\t\treturn driverValue, nil\n\tcase [16]byte:\n\t\tdriverValue, err := (&uuidValue{dialect: dialect, value: value}).Value()\n\t\tif err != nil {\n\t\t\tif dialect == DialectPostgres {\n\t\t\t\treturn nil, fmt.Errorf(\"converting %#v to string: %w\", value, err)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"converting %#v to bytes: %w\", value, err)\n\t\t}\n\t\treturn driverValue, nil\n\tcase driver.Valuer:\n\t\tdriverValue, err := value.Value()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"calling Value on %#v: %w\", value, err)\n\t\t}\n\t\treturn driverValue, nil\n\t}\n\treturn value, nil\n}\n"
  },
  {
    "path": "sq.md",
    "content": "# sq (Structured Query)\n\n<center>\n  <img src=\"https://i.imgur.com/GAxInSb.png\" title=\"code example of a select query using sq\" alt=\"code example of a select query using sq, to give viewers a quick idea of what the library is about\" style=\"max-width:90%;\">\n</center>\n\n## Introduction to sq #introduction\n\nGithub link: [github.com/bokwoon95/sq](https://github.com/bokwoon95/sq)\n\nsq is a type-safe data mapper and query builder for Go. It is not an ORM, but aims to be as convenient as an ORM while retaining the flexibility of a query builder/raw sql.\n\nNotable features:\n\n- Works across SQLite, Postgres, MySQL and SQL Server. [[more info](#set-query-dialect)]\n- Each dialect has its own query builder, allowing the full use of dialect-specific features. [[more info](#dialect-specific-features)]\n- Declarative schema migrations. [[more info](#declarative-schema)]\n- Supports arrays, enums, JSON and UUID. [[more info](#arrays-enums-json-uuid)]\n- Query logging. [[more info](#logging)]\n\n## Installation #installation\n\nThis package only supports Go 1.18 and above because it uses generics for data mapping.\n\n```shell\n$ go get github.com/bokwoon95/sq\n$ go install -tags=fts5 github.com/bokwoon95/sqddl@latest\n```\n\n## Quickstart #quickstart\n\nConnect to the database.\n\n```go\ndb, err := sql.Open(\"postgres\", \"postgres://username:password@localhost:5432/sakila?sslmode=disable\")\n```\n\nDefine your model structs(s).\n\n```go\ntype Actor struct {\n    ActorID    int\n    FirstName  string\n    LastName   string\n    LastUpdate time.Time\n}\n```\n\nUse one of the below three functions to run your query.\n\n- **FetchAll(db, query, rowmapper) ([]T, error)**\n  - Fetch all results from a query.\n  - Equivalent to [sql.Query](https://pkg.go.dev/database/sql#DB.Query).\n- **FetchOne(db, query, rowmapper) (T, error)**.\n  - Fetch one result from a query.\n  - Returns sql.ErrNoRows if no results.\n  - Equivalent to [sql.QueryRow](https://pkg.go.dev/database/sql#DB.QueryRow).\n- **Exec(db, query) (sq.Result, error)**.\n  - Executes a query.\n  - Returns the rows affected (and the last insert ID, if it is supported by the dialect).\n  - Equivalent to [sql.Exec](https://pkg.go.dev/database/sql#DB.Exec).\n\n### Select example #rawsql-select\n\n#### Fetch all #rawsql-fetch-all\n\n```sql\nSELECT actor_id, first_name, last_name FROM actor WHERE first_name = 'DAN'\n```\n\n```go\nactors, err := sq.FetchAll(db, sq.\n    Queryf(\"SELECT {*} FROM actor WHERE first_name = {}\", \"DAN\").\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.Int(\"actor_id\"),\n            FirstName: row.String(\"first_name\"),\n            LastName:  row.String(\"last_name\"),\n        }\n    },\n)\n```\n\n#### Fetch one #rawsql-fetch-one\n\n```sql\nSELECT actor_id, first_name, last_name FROM actor WHERE actor_id = 18\n```\n\n```go\nactor, err := sq.FetchOne(db, sq.\n    Queryf(\"SELECT {*} FROM actor WHERE actor_id = {}\", 18).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.Int(\"actor_id\"),\n            FirstName: row.String(\"first_name\"),\n            LastName:  row.String(\"last_name\"),\n        }\n    },\n)\n```\n\n#### Fetch cursor #rawsql-fetch-cursor\n\n```sql\nSELECT actor_id, first_name, last_name FROM actor WHERE first_name = 'DAN'\n```\n\n```go\ncursor, err := sq.FetchCursor(db, sq.\n    Queryf(\"SELECT {*} FROM actor WHERE first_name = {}\", \"DAN\").\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.Int(\"actor_id\"),\n            FirstName: row.String(\"first_name\"),\n            LastName:  row.String(\"last_name\"),\n        }\n    },\n)\nif err != nil {\n}\ndefer cursor.Close()\n\nvar actors []Actor\nfor cursor.Next() {\n    actor, err := cursor.Result()\n    if err != nil {\n    }\n    actors = append(actors, actor)\n}\n```\n\n#### Fetch exists #rawsql-fetch-exists\n\n```sql\nSELECT EXISTS (SELECT 1 FROM actor WHERE actor_id = 18)\n```\n\n```go\nexists, err := sq.FetchExists(db, sq.\n    Queryf(\"SELECT 1 FROM actor WHERE actor_id = {}\", 18).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n### Insert example #rawsql-insert\n\n#### Insert one #rawsql-insert-one\n\n```sql\nINSERT INTO actor (actor_id, first_name, last_name) VALUES (18, 'DAN', 'TORN')\n```\n\n```go\n_, err := sq.Exec(db, sq.\n    Queryf(\"INSERT INTO actor (actor_id, first_name, last_name) VALUES {}\", sq.RowValue{\n        18, \"DAN\", \"TORN\",\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n#### Insert many #rawsql-insert-many\n\n```sql\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (18, 'DAN', 'TORN'),\n    (56, 'DAN', 'HARRIS'),\n    (116, 'DAN', 'STREEP')\n```\n\n```go\n_, err := sq.Exec(db, sq.\n    Queryf(\"INSERT INTO actor (actor_id, first_name, last_name) VALUES {}\", sq.RowValues{\n        {18, \"DAN\", \"TORN\"},\n        {56, \"DAN\", \"HARRIS\"},\n        {166, \"DAN\", \"STREEP\"},\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n### Update example #rawsql-update\n\n```sql\nUPDATE actor SET first_name = 'DAN', last_name = 'TORN' WHERE actor_id = 18\n```\n\n```go\n_, err := sq.Exec(db, sq.\n    Queryf(\"UPDATE actor SET first_name = {}, last_name = {} WHERE actor_id = {}\",\n        \"DAN\", \"TORN\", 18,\n    ).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n### Delete example #rawsql-delete\n\n```sql\nDELETE FROM actor WHERE actor_id = 56\n```\n\n```go\n_, err := sq.Exec(db, sq.\n    Queryf(\"DELETE FROM actor WHERE actor_id = {}\", 56).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n## How the rowmapper works #rowmapper\n\nThe [FetchAll/FetchOne/FetchCursor examples in the quickstart](#rawsql-select) use a rowmapper function both as a way of indicating what fields should be selected, as well as encoding how each row should be procedurally mapped back to a model struct.\n\n```go\n// The rowmapper function signature should match func(*sq.Row) T.\nfunc(row *sq.Row) Actor {\n    return Actor{\n        ActorID:   row.Int(\"actor_id\"),\n        FirstName: row.String(\"first_name\"),\n        LastName:  row.String(\"last_name\"),\n    }\n}\n```\n\nTo go into greater detail, the rowmapper is first called in \"passive mode\" where the `sq.Row` records the fields needed by the SELECT query. Those fields are then injected back into the SELECT query ([via the `{*}` insertion point](#rawsql-select)) and the query is run for real. Then the rowmapper is called in \"active mode\" where each `sq.Row` method call actually returns a value from the underlying row. The `Actor` result returned by each rowmapper call is then appended into a slice. All this is done generically, so the rowmapper can yield any variable of type `T` and a slice `[]T` will be returned at the end.\n\n**The order in which you call the `sq.Row` methods must be deterministic and must not change between rowmapper invocations**. Don't put an `row.Int()` call inside an if-block, for example.\n\n### Static vs dynamic queries #static-vs-dynamic-queries\n\nThe [query examples in the quickstart](#rawsql-select) showcase dynamic queries, i.e. queries whose SELECT-ed fields are dynamically determined by the rowmapper. You can also write static queries, where the columns you SELECT are hardcoded into the query and the rowmapper references those fields by alias/name.\n\n```go\nactors, err := sq.FetchAll(db, sq.\n    Queryf(\"SELECT actor_id, first_name, last_name AS lname FROM actor WHERE first_name = {}\", \"DAN\").\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        fmt.Printf(\"%#v\\n\", row.Columns()) // []string{\"actor_id\", \"first_name\", \"lname\"}\n        fmt.Printf(\"%#v\\n\", row.Values())  // []any{18, \"DAN\", \"TORN\"}\n        return Actor{\n            ActorID:   row.Int(\"actor_id\"),\n            FirstName: row.String(\"first_name\"),\n            LastName:  row.String(\"lname\"),\n        }\n    },\n)\n```\n\n### Handling errors #rowmapper-handling-errors\n\nIf you do any computation in a rowmapper that returns an error, you can panic() with it and the error will be propagated as the error return value of FetchAll/FetchOne/FetchCursor. Try not to do anything that returns an error in the rowmapper.\n\n```go\nfunc(row *sq.Row) Film {\n    var film Film\n    film.FilmID = row.Int(\"film_id\")\n    film.Title = row.String(\"title\")\n    film.Description = row.String(\"description\")\n\n    // Pull raw bytes from the DB and unmarshal as JSON.\n    b := row.Bytes(\"special_features\")\n    err := json.Unmarshal(b, &film.SpecialFeatures)\n    if err != nil {\n        panic(err)\n    }\n\n    // Alternatively you can use row.JSON(), which doesn't\n    // require you to do error handling.\n    row.JSON(&film.SpecialFeatures, \"special_features\")\n\n    return film\n}\n```\n\n### Available methods #sq-row-methods\n\n```go\n// These methods are straighforward and return the type associated with their\n// name.\n//\n// NULL values are automatically converted to a zero value: 0 for numbers, the\n// empty string for strings, an nil slice for []byte, etc. Use the NullXXX\n// method variants if capturing NULL is meaningful to you.\nvar _ []byte    = row.Bytes(\"field_name\")\nvar _ bool      = row.Bool(\"field_name\")\nvar _ float64   = row.Float64(\"field_name\")\nvar _ int       = row.Int(\"field_name\")\nvar _ int64     = row.Int64(\"field_name\")\nvar _ string    = row.String(\"field_name\")\nvar _ time.Time = row.Time(\"field_name\")\n\n// The sql.NullXXX variants.\nvar _ sql.NullBool    = row.NullBool(\"field_name\")\nvar _ sql.NullFloat64 = row.NullFloat64(\"field_name\")\nvar _ sql.NullInt64   = row.NullInt64(\"field_name\")\nvar _ sql.NullString  = row.NullString(\"field_name\")\nvar _ sql.NullTime    = row.NullTime(\"field_name\")\n\n// row.Scan scans the value of field_name into a destination pointer. If the\n// pointer type implements sql.Scanner, this is where to use it.\nrow.Scan(dest, \"field_name\")\n\n// row.Array scans the value of field_name into a destination slice pointer. Only\n// *[]bool, *[]int64, *[]int32, *[]float64, *[]float32 and *[]string are\n// supported. On Postgres this value must be an array, while for other dialects\n// this value must be a JSON array.\nrow.Array(sliceDest, \"field_name\")\n\n// row.JSON scans the value of field_name into a destination pointer that\n// json.Unmarshal can unmarshal JSON into. The value must be JSON.\nrow.JSON(jsonDest, \"field_name\")\n\n// row.UUID scans the value of field_name into a destination pointer whose\n// underlying type must be [16]byte. The value can be BINARY(16) or a UUID string.\nrow.UUID(uuidDest, \"field_name\")\n```\n\nAdditionally there are also the `Field` method variants that accept an `sq.Field` instead of a `string` name. This is relevant if you are [using the query builder](#querybuilder) instead of [raw SQL](#rawsql-select).\n\n```go\nvar _ []byte    = row.BytesField(tbl.FIELD_NAME)\nvar _ bool      = row.BoolField(tbl.FIELD_NAME)\nvar _ float64   = row.Float64Field(tbl.FIELD_NAME)\nvar _ int       = row.IntField(tbl.FIELD_NAME)\nvar _ int64     = row.Int64Field(tbl.FIELD_NAME)\nvar _ string    = row.StringField(tbl.FIELD_NAME)\nvar _ time.Time = row.TimeField(tbl.FIELD_NAME)\n\nvar _ sql.NullBool    = row.NullBoolField(tbl.FIELD_NAME)\nvar _ sql.NullFloat64 = row.NullFloat64Field(tbl.FIELD_NAME)\nvar _ sql.NullInt64   = row.NullInt64Field(tbl.FIELD_NAME)\nvar _ sql.NullString  = row.NullStringField(tbl.FIELD_NAME)\nvar _ sql.NullTime    = row.NullTimeField(tbl.FIELD_NAME)\n\nrow.ScanField(dest, tbl.FIELD_NAME)\n\nrow.ArrayField(sliceDest, tbl.FIELD_NAME)\n\nrow.JSONField(jsonDest, tbl.FIELD_NAME)\n\nrow.UUIDField(uuidDest, tbl.FIELD_NAME)\n```\n\n## Setting the dialect of a query #set-query-dialect\n\nEach [sample query in the quickstart](#rawsql-select) has its dialect set to Postgres.\n\n```go\nsq.Queryf(\"SELECT {*} FROM actor WHERE first_name = {}\", \"DAN\").SetDialect(sq.DialectPostgres)\n```\n\nThis is to generate a Postgres-compatible query, where each curly brace `{}` placeholder is replaced with a Postgres dollar placeholder (e.g. $1, $2, $3). This is the same case for the [query builder](#querybuilder-select). You can choose one of four possible dialects:\n\n```go\nconst (\n    DialectSQLite    = \"sqlite\"    // placeholders are $1, $2, $3\n    DialectPostgres  = \"postgres\"  // placeholders are $1, $2, $3\n    DialectMySQL     = \"mysql\"     // placeholders are ?, ?, ?\n    DialectSQLServer = \"sqlserver\" // placeholders are @p1, @p2, @p3\n)\n```\n\nEach dialect that you pick will use the corresponding placeholder type when generating the query. [Ordinal placeholders (`{1}`, `{2}`, `{3}`) and named placeholders (`{foo}`, `{bar}`, `{baz}`)](#ordinal-named-placeholders) are also supported.\n\nYou can use the **sq.SQLite**, **sq.Postgres**, **sq.MySQL** and **sq.SQLServer** package-level variables as shorthand for setting the dialect (in order to type less).\n\n```go\nsq.SQLite.Queryf(query)    // sq.Queryf(query).SetDialect(sq.DialectSQLite)\nsq.Postgres.Queryf(query)  // sq.Queryf(query).SetDialect(sq.DialectPostgres)\nsq.MySQL.Queryf(query)     // sq.Queryf(query).SetDialect(sq.DialectMySQL)\nsq.SQLServer.Queryf(query) // sq.Queryf(query).SetDialect(sq.DialectSQLServer)\n```\n\n### Setting the query dialect globally #set-query-dialect-globally\n\nTo set the default dialect globally, set the value of sq.DefaultDialect. This\nvalue is used when no dialect is provided (i.e. an empty string).\n\n```go\nfunc init() {\n    // Sets the default dialect of all queries to Postgres (unless a dialect is\n    // explicitly provided).\n    //\n    // NOTE: You can't use a pointer to sq.DialectPostgres directly because it is\n    // a constant which cannot be addressed.\n    dialect := sq.DialectPostgres\n    sq.DefaultDialect.Store(&dialect)\n}\n```\n\n## sq's query templating syntax #templating-syntax\n\nsq.Queryf (and sq.Expr) use a Printf-style templating syntax where the format string uses curly brace `{}` placeholders. Here is a basic example for Queryf:\n\n> **Note**: All examples below interpolate their arguments into the SQL query for illustrative purposes, but in actuality the [proper prepared statement placeholders](#set-query-dialect) will be generated.\n\n```go\nsq.Queryf(\"SELECT first_name FROM actor WHERE actor_id = {}\", 18)\n```\n\n```sql\nSELECT first_name FROM actor WHERE actor_id = 18\n```\n\nsq.Queryf has an Append() method which allows for a basic level of query building:\n\n```go\nvar (\n    name  = \"bob\"\n    email = \"bob@email.com\"\n    age   = 27\n)\nq := sq.Queryf(\"SELECT name, email FROM tbl WHERE 1 = 1\") // https://stackoverflow.com/questions/1264681/what-is-the-purpose-of-using-where-1-1-in-sql-statements\nif name != \"\" {\n    q = q.Append(\"AND name = {}\", name)\n}\nif email != \"\" {\n    q = q.Append(\"AND email = {}\", email)\n}\nif age != 0 {\n    q = q.Append(\"AND age = {}\", age)\n}\n```\n\n```sql\nSELECT name, email FROM tbl WHERE 1 = 1 AND name = 'bob' AND email = 'bob@email.com' AND age = 27\n```\n\nUnlike with SQL prepared statements, the curly brace `{}` placeholders are allowed to change the structure of a query (i.e. it can appear anywhere inside a query):\n\n```go\nsq.Queryf(\n    \"SELECT {} FROM {} WHERE first_name = {}\",\n    sq.Fields{\n        sq.Expr(\"actor_id\"),\n        sq.Expr(\"last_name\"),\n    },\n    sq.Expr(\"actor\"),\n    \"DAN\",\n)\n```\n\n```sql\nSELECT actor_id, last_name FROM actor WHERE first_name = 'DAN'\n```\n\n### Escaping the curly brace #escaping-curly-brace\n\nIf you wish to actually use curly braces `{}` inside the format string (which is very rare), you must escape the opening curly brace by doubling it up like this: `{{}`.\n\n```go\nsq.Queryf(\"SELECT '{{}', '{{abcd}'\")\n```\n\n```sql\nSELECT '{}', '{abcd}'\n```\n\n### Value expansion #value-expansion\n\nEach value passed to the query preprocessor is evaluated based on the following cases in the order shown:\n1. If the value [implements the `SQLWriter` interface](#sqlwriter), its `WriteSQL` method is called.\n2. Else if the value is a slice, the slice is expanded into a comma separated list.\n    - Each item in this list is further evaluated recursively following the same logic.\n    - byte slices (`[]byte`) are the exception, they are treated as a unit and do not undergo slice expansion.\n3. Otherwise, a dialect-appropriate placeholder is appended to the query string and the value itself is appended to the args.\n\nHere is an example of the three different cases in action.\n\n```go\nsq.Queryf(\n    \"SELECT {} FROM actor WHERE actor_id IN ({}) AND first_name = {}\",\n    // case 1\n    sq.Expr(\"jsonb_build_object({})\", []any{ // case 2\n        sq.Literal(\"first_name\"), // case 1\n        sq.Expr(\"first_name\"),    // case 1\n        sq.Literal(\"last_name\"),  // case 1\n        sq.Expr(\"last_name\"),     // case 1\n    }),\n    // case 2\n    []int{18, 56, 116},\n    // case 3\n    \"DAN\",\n).SetDialect(sq.DialectPostgres)\n```\n\n```sql\nSELECT jsonb_build_object('first_name', first_name, 'last_name', last_name)\nFROM actor\nWHERE actor_id IN ($1, $2, $3) AND first_name = $4\n-- args: 18, 56, 11, 'DAN'\n```\n\n### Ordinal and Named placeholders #ordinal-named-placeholders\n\nThe templating syntax supports 3 types of placeholders:\n1. Anonymous placeholders `{}`.\n2. Ordinal placeholders `{1}`, `{2}`, `{3}`.\n    - Ordinal placeholders used 1-based indexing.\n3. Named placeholders `{foo}`, `{bar}`, `{baz}`.\n    - Named placeholders in the format string must have a corresponding `sql.Named` value.\n    - Placeholder names must consist only of unicode letters, numbers `0-9` or underscore `_`.\n\nIt is possible for an anonymous placeholder, an ordinal placeholder and a named placeholder to refer to the same value.\n\n```go\nsq.Queryf(\"SELECT {}, {2}, {}, {name}\", \"Marco\", sql.Named(\"name\", \"Polo\"))\n//                    └─────────────┘\n//                   All refer to 'Polo'\n```\n\n```sql\nSELECT 'Marco', 'Polo', 'Polo', 'Polo'\n```\n\n#### Anonymous parameter example #anonymous-params\n\n```go\nsq.SQLite.Queryf(   \"SELECT {}, {}, {}\", \"foo\", \"bar\", \"foo\") // SQLite\nsq.Postgres.Queryf( \"SELECT {}, {}, {}\", \"foo\", \"bar\", \"foo\") // Postgres\nsq.MySQL.Queryf(    \"SELECT {}, {}, {}\", \"foo\", \"bar\", \"foo\") // MySQL\nsq.SQLServer.Queryf(\"SELECT {}, {}, {}\", \"foo\", \"bar\", \"foo\") // SQLServer\n```\n\n```sql\nSELECT $1, $2, $3    -- SQLite,    Args: 'foo', 'bar', 'foo'\nSELECT $1, $2, $3    -- Postgres,  Args: 'foo', 'bar', 'foo'\nSELECT ?, ?, ?       -- MySQL,     Args: 'foo', 'bar', 'foo'\nSELECT @p1, @p2, @p3 -- SQLServer, Args: 'foo', 'bar', 'foo'\n```\n\n#### Ordinal parameter example #ordinal-params\n\n```go\nsq.SQLite.Queryf(   \"SELECT {1}, {2}, {1}\", \"foo\", \"bar\") // SQLite\nsq.Postgres.Queryf( \"SELECT {1}, {2}, {1}\", \"foo\", \"bar\") // Postgres\nsq.MySQL.Queryf(    \"SELECT {1}, {2}, {1}\", \"foo\", \"bar\") // MySQL\nsq.SQLServer.Queryf(\"SELECT {1}, {2}, {1}\", \"foo\", \"bar\") // SQLServer\n```\n\n```sql\nSELECT $1, $2, $1    -- SQLite,    Args: 'foo', 'bar'\nSELECT $1, $2, $1    -- Postgres,  Args: 'foo', 'bar'\nSELECT ?, ?, ?       -- MySQL,     Args: 'foo', 'bar', 'foo'\nSELECT @p1, @p2, @p1 -- SQLServer, Args: 'foo', 'bar'\n```\n\n#### Named parameter example #named-params\n\n```go\n// SQLite\nsq.SQLite.Queryf(\"SELECT {one}, {two}, {one}\",\n    sql.Named(\"one\", \"foo\"),\n    sql.Named(\"two\", \"bar\"),\n)\n// Postgres\nsq.Postgres.Queryf(\"SELECT {one}, {two}, {one}\",\n    sql.Named(\"one\", \"foo\"),\n    sql.Named(\"two\", \"bar\"),\n)\n// MySQL\nsq.MySQL.Queryf(\"SELECT {one}, {two}, {one}\",\n    sql.Named(\"one\", \"foo\"),\n    sql.Named(\"two\", \"bar\"),\n)\n// SQLServer\nsq.SQLServer.Queryf(\"SELECT {one}, {two}, {one}\",\n    sql.Named(\"one\", \"foo\"),\n    sql.Named(\"two\", \"bar\"),\n)\n```\n\n```sql\nSELECT $one, $two, $one -- SQLite,    Args: one: 'foo', two: 'bar'\nSELECT $1, $2, $1       -- Postgres,  Args: 'foo', 'bar'\nSELECT ?, ?, ?          -- MySQL,     Args: 'foo', 'bar', 'foo'\nSELECT @one, @two, @one -- SQLServer, Args: one: 'foo', two: 'bar'\n```\n\n### SQLWriter example #sqlwriter\n\nAn SQLWriter represents anything that can render itself as SQL. It is the first thing taken into consideration during [value expansion](#value-expansion).\n\nHere is the definition of the SQLWriter interface:\n\n```go\ntype SQLWriter interface {\n    WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error\n}\n```\n\nAs an example, we will create a custom SQLWriter component that renders itself as string `str` for `num` times, where `str` and `num` are parameters:\n\n```go\nsq.Queryf(\"SELECT {}\", multiplier{str: \"lorem ipsum\", num: 5, delim: \" \"})\n```\n\n```sql\nSELECT lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum\n```\n\nThis is the implementation of `multiplier`:\n\n```go\ntype multiplier struct {\n    str   string\n    num   int\n    delim string\n}\n\nfunc (m multiplier) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n    for i := 0; i < m.num; i++ {\n        if i > 0 {\n            buf.WriteString(m.delim)\n        }\n        buf.WriteString(m.str)\n    }\n    return nil\n}\n```\n\n```go\nsq.Queryf(\"SELECT {}\", multiplier{str: \"foo\",         num: 3, delim: \"AND\"})\nsq.Queryf(\"SELECT {}\", multiplier{str: \"lorem ipsum\", num: 4, delim: \", \"})\nsq.Queryf(\"SELECT {}\", multiplier{str: \"🎉\",          num: 6, delim: \"\"})\n```\n\n```sql\nSELECT foo AND foo AND foo\nSELECT lorem ipsum, lorem ipsum, lorem ipsum, lorem ipsum\nSELECT 🎉🎉🎉🎉🎉🎉\n```\n\n## Using the query builder #querybuilder\n\n### Table structs #table-structs\n\nTo use a query builder, you need to first define your table struct(s).\n\n```go\ntype ACTOR struct {\n    sq.TableStruct // A table struct is marked by embedding sq.TableStruct as the first field.\n    ACTOR_ID    sq.NumberField\n    FIRST_NAME  sq.StringField\n    LAST_NAME   sq.StringField\n    LAST_UPDATE sq.TimeField\n}\n```\n\nYou can then instantiate the table using [sq.New()](https://pkg.go.dev/github.com/bokwoon95/sq#New) and use it to create predicates and participate in a query.\n\n```go\na := sq.New[ACTOR](\"a\")\n// actor AS a\n\na.ACTOR_ID.EqInt(18)\n// a.actor_id = 18\n\na.LAST_UPDATE.IsNotNull()\n// a.last_update IS NOT NULL\n\nsq.Select(a.FIRST_NAME, a.LAST_NAME).From(a).Where(a.ACTOR_ID.In([]int{18, 56, 116}))\n// SELECT a.first_name, a.last_name FROM actor AS a WHERE a.actor_id IN (18, 56, 116)\n```\n\n#### Model structs #model-structs\n\nIn general, there should be two types of structs that you use with the query builder. One is the table struct, which represents an instance of an SQL table. The other is a model struct, which represents an instance of a domain model (in this example, an actor).\n\n```go\n// Table struct (represents your SQL table).\ntype ACTOR struct {\n    sq.TableStruct `sq:\"Actor\"`\n    ACTOR_ID       sq.NumberField `sq:\"ActorID\"`\n    FIRST_NAME     sq.StringField `sq:\"FirstName\"`\n    LAST_NAME      sq.StringField `sq:\"LastName\"`\n    LAST_UPDATE    sq.TimeField   `sq:\"LastUpdate\"`\n}\n\n// Model struct (represents an instance of an actor).\ntype Actor struct {\n    ActorID    int\n    FirstName  string\n    LastName   string\n    LastUpdate time.Time\n}\n\n// Note the different casing of ACTOR vs Actor.\na := sq.New[ACTOR](\"a\")\nactors, err := sq.FetchAll(db, sq.\n    From(a).\n    Where(a.FIRST_NAME.EqString(\"DAN\")).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.IntField(a.ACTOR_ID),\n            FirstName: row.StringField(a.FIRST_NAME),\n            LastName:  row.StringField(a.LAST_NAME),\n        }\n    },\n)\n```\n\n### Available Field types #field-types\n\nThere are 10 available field types that you can use in your [table structs](#table-structs).\n\n- **NumberField** (`int`, `int64`, INT, BIGINT, NUMERIC, etc)\n- **StringField** (`string`, TEXT, VARCHAR, etc)\n- **TimeField** (`time.Time`, DATE, DATETIME, TIMESTAMP, etc)\n- **BooleanField** (`bool`, BOOLEAN, TINYINT, BIT, etc)\n- **BinaryField** (`[]byte`, BYTEA, BINARY, etc)\n- **ArrayField**\n    - Represents a primitive slice type in Go (`[]string`, `[]int64`, `[]int32`, `[]float64`, `[]float32`, `[]bool`)\n    - In Postgres, this is a native array (TEXT[], INT[], BIGINT[], NUMERIC[], BOOLEAN[])\n    - In other databases, this is a JSON array.\n- **EnumField**\n    - Represents an \"enum\" type in Go (`iota`, `string`, take your pick)\n    - In Postgres, this is a native enum type (CREATE TYPE AS ENUM)\n    - In other databases, this is a plain string.\n    - Your Go enum type must [implement the `Enumeration` interface](#enums).\n- **JSONField**\n    - Represents a Go type that works with `json.Marshal` and `json.Unmarshal`.\n    - In Postgres, this is the JSONB or JSON type.\n    - In MySQL, this is the JSON type.\n    - In other databases, this is a plain string.\n- **UUIDField**\n    - Represents any type whose underlying type is [16]byte in Go.\n    - In Postgres, this is a UUID.\n    - In other databases, this is a BINARY(16).\n- **AnyField**\n    - A catch-all field type that can substitute as any of the 9 other field types.\n    - Use this to represent types like `TSVECTOR` that don't have a corresponding representation.\n\n### Field name to column name translation #field-name-translation\n\nThe table name and column names are derived by lowercasing the struct name and struct field names. So a struct `ACTOR` will be translated to a table called `actor`, and a field `ACTOR_ID` will be translated to a column called `actor_id`. If that is not what you want, you can specify the desired name inside an `sq` struct tag.\n\n```go\ntype ACTOR struct {\n    sq.TableStruct `sq:\"Actor\"`\n    ACTOR_ID       sq.NumberField `sq:\"ActorID\"`\n    FIRST_NAME     sq.StringField `sq:\"FirstName\"`\n    LAST_NAME      sq.StringField `sq:\"LastName\"`\n    LAST_UPDATE    sq.TimeField   `sq:\"LastUpdate\"`\n}\n\na := sq.New[ACTOR](\"\") // \"Actor\"\na.ACTOR_ID             // \"Actor\".\"ActorID\"\na.FIRST_NAME           // \"Actor\".\"FirstName\"\n```\n\n### Aliasing a table struct #alias-table-struct\n\nsq.New() takes in an alias string as an argument and returns a table with that alias. Leave the alias string blank if you don't want the table to have an alias.\n\n```go\na1 := sq.New[ACTOR](\"a\") // actor AS a\na1.ACTOR_ID              // a.actor_id\n\na2 := sq.New[ACTOR](\"\") // actor\na2.ACTOR_ID             // actor.actor_id\n```\n\n### Table structs as a declarative schema #declarative-schema\n\n#### Generating migrations #generating-migrations\n\n> Example: here is [the table struct representation](https://github.com/bokwoon95/sqddl/blob/main/ddl/testdata/tables.go.txt) of the [sakila database schema](https://www.jooq.org/sakila).\n\nYour [table structs](#table-structs) serve as a declarative schema for your tables. The [sqddl tool](https://bokwoon.neocities.org/sqddl.html) is able to parse Go files containing table structs and [generate the necessary migrations](https://bokwoon.neocities.org/sqddl.html#generate) needed to reach that desired schema. The generated migrations can then be [applied using the same sqddl tool](https://bokwoon.neocities.org/sqddl.html#migrate).\n\n```shell\n# Generate migrations needed to go from $DATABASE_URL to tables/tables.go and write into ./migrations dir\n$ sqddl generate -src \"$DATABASE_URL\" -dest tables/tables.go -output-dir ./migrations\n\n# Apply the pending migrations in ./migrations dir against the database $DATABASE_URL\n$ sqddl migrate -db \"$DATABASE_URL\" -dir ./migrations\n```\n\nFor more information on how to express \"CREATE TABLE\" DDL using tables structs, please check out the [sqddl documentation](https://bokwoon.neocities.org/sqddl.html#table-structs).\n\n#### Generating table structs #generating-table-structs\n\nThe reverse is also possible, you can [generate table structs from an existing database](https://bokwoon.neocities.org/sqddl.html#tables). If you have an existing database this is the recommended way to get started, rather than creating the table structs manually to match the database.\n\n```shell\n# Generate table structs from $DATABASE_URL and write into tables/tables.go\n$ sqddl tables -db \"$DATABASE_URL\" -file tables/tables.go\n```\n\nOnce you have your table structs, you can edit your table structs and [generate migrations](#generating-migrations) from them. Note that migration generation only covers [a subset of possible DDL operations](#) so it's possible that you will have to write some migrations by hand.\n\n### Select example #querybuilder-select\n\n#### Fetch all #querybuilder-fetch-all\n\n```sql\nSELECT a.actor_id, a.first_name, a.last_name FROM actor AS a WHERE a.first_name = 'DAN'\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nactors, err := sq.FetchAll(db, sq.\n    From(a).\n    Where(a.FIRST_NAME.EqString(\"DAN\")).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:    row.IntField(a.ACTOR_ID),\n            FirstName:  row.StringField(a.FIRST_NAME),\n            LastName:   row.StringField(a.LAST_NAME),\n        }\n    },\n)\n```\n\n#### Fetch one #querybuilder-fetch-one\n\n```sql\nSELECT a.actor_id, a.first_name, a.last_name FROM actor AS a WHERE a.actor_id = 18\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nactor, err := sq.FetchOne(db, sq.\n    From(a).\n    Where(a.ACTOR_ID.EqInt(18)).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:    row.IntField(a.ACTOR_ID),\n            FirstName:  row.StringField(a.FIRST_NAME),\n            LastName:   row.StringField(a.LAST_NAME),\n        }\n    },\n)\n```\n\n#### Fetch cursor #querybuilder-fetch-cursor\n\n```sql\nSELECT a.actor_id, a.first_name, a.last_name FROM actor AS a WHERE a.first_name = 'DAN'\n```\n\n```go\na := sq.New[ACTOR](\"a\")\ncursor, err := sq.FetchCursor(db, sq.\n    From(a).\n    Where(a.FIRST_NAME.EqString(\"DAN\")).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:    row.IntField(a.ACTOR_ID),\n            FirstName:  row.StringField(a.FIRST_NAME),\n            LastName:   row.StringField(a.LAST_NAME),\n        }\n    },\n)\nif err != nil {\n}\ndefer cursor.Close()\n\nvar actor []Actor\nfor cursor.Next() {\n    actor, err := cursor.Result()\n    if err != nil {\n    }\n    actors = append(actors, actor)\n}\n```\n\n#### Fetch exists #querybuilder-fetch-exists\n\n```sql\nSELECT EXISTS (SELECT 1 FROM actor AS a WHERE a.actor_id = 18)\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nexists, err := sq.FetchExists(db, sq.\n    SelectOne().\n    From(a).\n    Where(a.ACTOR_ID.EqInt(18)).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n#### Fetch distinct #querybuilder-fetch-distinct\n\n```sql\nSELECT DISTINCT a.first_name FROM actor AS a\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nfirstNames, err := sq.FetchAll(db, sq.\n    SelectDistinct().\n    From(a).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) string {\n        return row.String(a.FIRST_NAME)\n    },\n)\n```\n\n### Insert example #querybuilder-insert\n\n#### Insert one #querybuilder-insert-one\n\n```sql\nINSERT INTO actor (actor_id, first_name, last_name) VALUES (18, 'DAN', 'TORN')\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Values(18, \"DAN\", \"TORN\").\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n#### Insert many #querybuilder-insert-many\n\n```sql\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (18, 'DAN', 'TORN'),\n    (56, 'DAN', 'HARRIS'),\n    (116, 'DAN', 'STREEP')\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Values(18, \"DAN\", \"TORN\").\n    Values(56, \"DAN\", \"HARRIS\").\n    Values(166, \"DAN\", \"STREEP\").\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n#### Insert from Select #querybuilder-insert-from-select\n\n```sql\nINSERT INTO actor (actor_id, first_name, last_name)\nSELECT actor.actor_id, actor.first_name, actor.last_name\nFROM actor\nWHERE actor.last_update IS NOT NULL\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Select(sq.\n        Select(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n        From(a).\n        Where(a.LAST_UPDATE.IsNotNull()),\n    ).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n#### Insert one (column mapper) #querybuilder-insert-one-columnmapper\n\n```sql\nINSERT INTO actor (actor_id, first_name, last_name) VALUES (18, 'DAN', 'TORN')\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.\n    InsertInto(a).\n    ColumnValues(func(col *sq.Column) {\n        col.SetInt(a.ACTOR_ID, 18)\n        col.SetString(a.FIRST_NAME, \"DAN\")\n        col.SetString(a.LAST_NAME, \"TORN\")\n        return nil\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n#### Insert many (column mapper) #querybuilder-insert-many-columnmapper\n\n```sql\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (18, 'DAN', 'TORN'),\n    (56, 'DAN', 'HARRIS'),\n    (116, 'DAN', 'STREEP')\n```\n\n```go\nactors := []Actor{\n    {ActorID: 18, FirstName: \"DAN\", LastName: \"TORN\"},\n    {ActorID: 56, FirstName: \"DAN\", LastName: \"HARRIS\"},\n    {ActorID: 166, FirstName: \"DAN\", LastName: \"STREEP\"},\n}\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.\n    InsertInto(a).\n    ColumnValues(func(col *sq.Column) {\n        for _, actor := range actors {\n            col.SetInt(a.ACTOR_ID, actor.ActorID)\n            col.SetString(a.FIRST_NAME, actor.FirstName)\n            col.SetString(a.LAST_NAME, actor.LastName)\n        }\n        return nil\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n#### How does the Insert column mapper work? #insert-columnmapper\n\nThe Insert column mapper works by having the `sq.Column` note down the very first field passed to it. Everytime `sq.Column` sees that field again, it will treat it as starting a new row value.\n\n```go\na := sq.New[ACTOR](\"\")\nq := sq.\n    InsertInto(a).\n    ColumnValues(func(col *sq.Column) {\n        col.SetInt(a.ACTOR_ID, 1) // every a.ACTOR_ID will mark the start of a new row value\n        col.SetString(a.FIRST_NAME, \"PENELOPE\")\n        col.SetString(a.LAST_NAME, \"GUINESS\")\n\n        col.SetInt(a.ACTOR_ID, 2)\n        col.SetString(a.FIRST_NAME, \"NICK\")\n        col.SetString(a.LAST_NAME, \"WAHLBERG\")\n\n        col.SetInt(a.ACTOR_ID, 3)\n        col.SetString(a.FIRST_NAME, \"ED\")\n        col.SetString(a.LAST_NAME, \"CHASE\")\n        return nil\n    }).\n    SetDialect(sq.DialectPostgres)\n```\n\n```sql\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (1, 'PENELOPE', 'GUINESS'),\n    (2, 'NICK', 'WAHLBERG'),\n    (3, 'ED', 'CHASE')\n```\n\n### Update example #querybuilder-update\n\n```sql\nUPDATE actor SET first_name = 'DAN', last_name = 'TORN' WHERE actor.actor_id = 18\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.\n    Update(a).\n    Set(\n        a.FIRST_NAME.SetString(\"DAN\"),\n        a.LAST_NAME.SetString(\"TORN\"),\n    ).\n    Where(a.ACTOR_ID.EqInt(18)).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n#### Update (column mapper) #update-columnmapper\n\n```sql\nUPDATE actor SET first_name = 'DAN', last_name = 'TORN' WHERE actor.actor_id = 18\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.\n    Update(a).\n    SetFunc(func(col *sq.Column) {\n        col.SetString(a.FIRST_NAME, \"DAN\")\n        col.SetString(a.LAST_NAME, \"TORN\")\n        return nil\n    }).\n    Where(a.ACTOR_ID.EqInt(18)).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n### Delete example #querybuilder-delete-example\n\n```sql\nDELETE FROM actor WHERE actor.actor_id = 56\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.\n    DeleteFrom(a).\n    Where(a.ACTOR_ID.EqInt(56)).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n### Combining predicates (AND and OR) #combining-predicates\n\n`Where()` accepts more than one predicate. By default, those predicates are `AND`-ed together.\n\n```go\na := sq.New[ACTOR](\"a\")\nquery := sq.\n    Select(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    From(a).\n    Where(\n        a.FIRST_NAME.EqString(\"BOB\"),\n        a.LAST_NAME.EqString(\"THE BUILDER\"),\n        a.LAST_UPDATE.IsNotNull(),\n    )\n```\n\n```sql\nSELECT a.actor_id, a.first_name, a.last_name\nFROM actor AS a\nWHERE a.first_name = 'BOB' AND a.last_name = 'THE BUILDER' AND a.last_update IS NOT NULL\n```\n\nIf you need to `OR` those predicates together, wrap them in `sq.Or()`.\n\n```go\na := sq.New[ACTOR](\"a\")\nquery := sq.\n    Select(a.actor_id, a.FIRST_NAME, a.LAST_NAME).\n    From(a).\n    Where(sq.Or( // <-- sq.Or\n        a.FIRST_NAME.EqString(\"BOB\"),\n        a.LAST_NAME.EqString(\"THE BUILDER\"),\n        a.LAST_UPDATE.IsNotNull(),\n    ))\n```\n\n```sql\nSELECT a.actor_id, a.first_name, a.last_name\nFROM actor AS a\nWHERE a.first_name = 'BOB' OR a.last_name = 'THE BUILDER' OR a.last_update IS NOT NULL\n```\n\n### Using expressions in the query builder #expr\n\nIf you need to do SQL math or call an SQL function, you need to use sq.Expr() to create an expression. [The same query templating syntax](#templating-syntax) in sq.Queryf() can be used here.\n\n```sql\nSELECT a.first_name || ' ' || a.last_name AS fullname\nFROM actor AS a\nWHERE a.actor_id IN (18, 56, 116)\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nq := sq.\n    Select(sq.Expr(\"{} || ' ' || {}\", a.FIRST_NAME, a.LAST_NAME).As(\"fullname\")).\n    From(a).\n    Where(a.ACTOR_ID.In([]int{18, 56, 116})).\n    SetDialect(sq.DialectPostgres)\n```\n\nsq.Expr() satisfies the `Any` interface and can be used wherever a `Number`, `String`, `Time`, `Boolean`, `Binary`, `Array`, `Enum`, `JSON` or `UUID` interface is expected.\n\n#### Dialect expressions #dialect-expr\n\nSometimes a query may be the same across different dialects save for some dialect-specific function call or expression, which changes for each dialect. In those cases you can use sq.DialectExpr() to use different expressions depending on the dialect.\n\n```sql\n-- The 3 queries below are nearly identical except for the name of their JSON\n-- aggregation function.\n\n-- SQLite\nSELECT json_group_array(a.last_name)\nFROM actor AS a WHERE a.first_name = 'DAN'\n\n-- Postgres\nSELECT json_agg(a.last_name)\nFROM actor AS a WHERE a.first_name = 'DAN'\n\n-- MySQL\nSELECT json_arrayagg(a.last_name)\nFROM actor AS a WHERE a.first_name = 'DAN'\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nq := sq.\n    Select(\n        sq.DialectExpr(\"json_group_array({})\", a.LAST_NAME).                // default case\n            DialectExpr(sq.DialectPostgres, \"json_agg({})\", a.LAST_NAME).   // if dialect == sq.DialectPostgres\n            DialectExpr(sq.DialectMySQL, \"json_arrayagg({})\", a.LAST_NAME), // if dialect == sq.DialectMySQL\n    ),\n    From(a).\n    Where(a.FIRST_NAME.EqString(\"DAN\")).\n    SetDialect(dialect)\n```\n\nSimilar to sq.Expr(), sq.DialectExpr() can be used wherever a `Number`, `String`, `Time`, `Boolean`, `Binary`, `Array`, `Enum`, `JSON` or `UUID` interface is expected.\n\n## How do I use dialect-specific features? #dialect-specific-features\n\nThere are dialect-specific query builders for each dialect that are accessible through the four package-level variables:\n- **sq.SQLite**\n- **sq.Postgres**\n- **sq.MySQL**\n- **sq.SQLServer**\n\nDo note that you can also use the dialect-agnostic query builder ([as shown in the query builder examples)](#querybuilder-select) if you're not using any dialect-specific features. Doing so will make your queries more portable, as you can just [toggle the dialect on the query](#set-query-dialect) and have it work across multiple databases without effort.\n\n### SQLite-specific features #sqlite-specific-features\n\n#### RETURNING #sqlite-returning\n\n```sql\nINSERT INTO actor\n    (first_name, last_name)\nVALUES\n    ('PENELOPE', 'GUINESS'),\n    ('NICK', 'WAHLBERG'),\n    ('ED', 'CHASE')\nRETURNING\n    actor.actor_id, actor.first_name, actor.last_name\n```\n\n```go\na := sq.New[ACTOR](\"\")\nactors, err := sq.FetchAll(db, sq.SQLite.\n    InsertInto(a).\n    Columns(a.FIRST_NAME, a.LAST_NAME).\n    Values(\"PENELOPE\", \"GUINESS\").\n    Values(\"NICK\", \"WAHLBERG\").\n    Values(\"ED\", \"CHASE\"),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.IntField(a.ACTOR_ID),\n            FirstName: row.StringField(a.FIRST_NAME),\n            LastName:  row.StringField(a.LAST_NAME),\n        }\n    },\n)\n```\n\n#### LastInsertId #sqlite-last-insert-id\n\n```sql\nINSERT INTO actor (first_name, last_name) VALUES ('PENELOPE', 'GUINESS');\nSELECT last_insert_rowid();\n```\n\n```go\na := sq.New[ACTOR](\"\")\nresult, err := sq.Exec(db, sq.SQLite.\n    InsertInto(a).\n    Columns(a.FIRST_NAME, a.LAST_NAME).\n    Values(\"PENELOPE\", \"GUINESS\"),\n)\nif err != nil {\n}\nfmt.Println(result.LastInsertId) // int64\n```\n\n#### Insert ignore duplicates #sqlite-insert-ignore-duplicates\n\n```sql\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (1, 'PENELOPE', 'GUINESS'),\n    (2, 'NICK', 'WAHLBERG'),\n    (3, 'ED', 'CHASE')\nON CONFLICT DO NOTHING\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.SQLite.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Values(1, \"PENELOPE\", \"GUINESS\").\n    Values(2, \"NICK\", \"WAHLBERG\").\n    Values(3, \"ED\", \"CHASE\").\n    OnConflict().DoNothing(),\n)\n```\n\n#### Upsert #sqlite-upsert\n\n```sql\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (1, 'PENELOPE', 'GUINESS'),\n    (2, 'NICK', 'WAHLBERG'),\n    (3, 'ED', 'CHASE')\nON CONFLICT (actor_id) DO UPDATE SET\n    first_name = EXCLUDED.first_name,\n    last_name = EXCLUDED.last_name\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.SQLite.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Values(1, \"PENELOPE\", \"GUINESS\").\n    Values(2, \"NICK\", \"WAHLBERG\").\n    Values(3, \"ED\", \"CHASE\").\n    OnConflict(a.ACTOR_ID).DoUpdateSet(\n        a.FIRST_NAME.Set(a.FIRST_NAME.WithPrefix(\"EXCLUDED\")),\n        a.LAST_NAME.Set(a.LAST_NAME.WithPrefix(\"EXLCUDED\")),\n    ),\n)\n```\n\n#### Update with Join #sqlite-update-with-join\n\n```sql\nUPDATE actor\nSET last_name = 'DINO'\nFROM film_actor\nJOIN film ON film.film_id = film_actor.film_id\nWHERE film_actor.actor_id = actor.actor_id AND film.title = 'ACADEMY DINOSAUR'\n```\n\n```go\na, fa, f := sq.New[ACTOR](\"\"), sq.New[FILM_ACTOR](\"\"), sq.New[FILM](\"\")\n_, err := sq.Exec(db, sq.SQLite.\n    Update(a).\n    Set(a.LAST_NAME.SetString(\"DINO\")).\n    From(fa).\n    Join(f, f.FILM_ID.Eq(fa.FILM_ID)).\n    Where(\n        fa.ACTOR_ID.Eq(a.ACTOR_ID),\n        f.TITLE.EqString(\"ACADEMY DINOSAUR\"),\n    ),\n)\n```\n\n#### Delete with Join #sqlite-delete-with-join\n\nThis is not technically an SQLite-specific feature as it uses a plain subquery to achieve a Delete with Join. Other databases have their own dialect-specific way of doing this, but this method works across every database and as such I prefer it over the others.\n\n```sql\nDELETE FROM actor\nWHERE EXISTS (\n    SELECT 1\n    FROM film_actor\n    JOIN film ON film.film_id = film_actor.film_id\n    WHERE film_actor.actor_id = actor.actor_id AND film.title = 'ACADEMY DINOSAUR'\n)\n```\n\n```go\na, fa, f := sq.New[ACTOR](\"\"), sq.New[FILM_ACTOR](\"\"), sq.New[FILM](\"\")\n_, err := sq.Exec(db, sq.SQLite.\n    DeleteFrom(a).\n    Where(sq.Exists(sq.\n        SelectOne().\n        From(fa).\n        Join(f, f.FILM_ID.Eq(f.FILM_ID)).\n        Where(\n            fa.ACTOR_ID.Eq(a.ACTOR_ID),\n            f.TITLE.EqString(\"ACADEMY DINOSAUR\"),\n        ),\n    )),\n)\n```\n\n#### Bulk Update #sqlite-bulk-update\n\n```sql\nUPDATE actor\nSET\n    first_name = tmp.first_name,\n    last_name = tmp.last_name\nFROM (\n        SELECT 1 AS actor_id, 'PENELOPE' AS first_name, 'GUINESS' AS last_name\n        UNION ALL\n        SELECT 2, 'NICK', 'WAHLBERG'\n        UNION ALL\n        SELECT 3, 'ED', 'CHASE'\n    ) AS tmp\nWHERE tmp.actor_id = actor.actor_id\n```\n\n```go\na := sq.New[ACTOR](\"\")\ntmp := sq.SelectValues{\n    Alias:     \"tmp\",\n    Columns:   []string{\"actor_id\", \"first_name\", \"last_name\"},\n    RowValues: [][]any{\n        {1, \"PENELOPE\", \"GUINESS\"},\n        {2, \"NICK\", \"WAHLBERG\"},\n        {3, \"ED\", \"CHASE\"},\n    },\n}\n_, err := sq.Exec(db, sq.SQLite.\n    Update(a).\n    Set(\n        a.FIRST_NAME.Set(tmp.Field(\"first_name\")),\n        a.LAST_NAME.Set(tmp.Field(\"last_name\")),\n    ).\n    From(tmp).\n    Where(tmp.Field(\"actor_id\").Eq(a.ACTOR_ID)),\n)\n```\n\n### Postgres-specific features #postgres-specific-features\n\n#### DISTINCT ON #postgres-distinct-on\n\n```sql\nSELECT DISTINCT ON (a.first_name) a.first_name, a.last_name\nFROM actor AS a\nORDER BY a.first_name\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nactors, err := sq.FetchAll(db, sq.Postgres.\n    From(a).\n    DistinctOn(a.FIRST_NAME).\n    OrderBy(a.FIRST_NAME),\n    func(row *sq.Row) Actor {\n        return Actor{\n            FirstName: row.String(a.FIRST_NAME),\n            LastName:  row.String(a.LAST_NAME),\n        }\n    },\n)\n```\n\n#### FETCH NEXT, WITH TIES #postgres-fetch-next-with-ties\n\n```sql\nSELECT a.first_name\nFROM actor AS a\nOFFSET 5\nFETCH NEXT 10 ROWS WITH TIES\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nfirstNames, err := sq.FetchAll(db, sq.Postgres.\n    From(a).\n    Offset(5).\n    FetchNext(10).WithTies(),\n    func(row *sq.Row) string {\n        return row.String(a.FIRST_NAME)\n    },\n)\n```\n\n#### FOR UPDATE, FOR SHARE #postgres-for-update-for-share\n\n**For Update**\n\n```sql\nSELECT a.actor_id, a.first_name, a.last_name\nFROM actor AS a\nWHERE a.first_name = 'DAN'\nFOR UPDATE SKIP LOCKED\n```\n\n```go\nactors, err := sq.FetchAll(db, sq.Postgres.\n    From(a).\n    Where(a.FIRST_NAME.EqString(\"DAN\")).\n    LockRows(\"FOR UPDATE SKIP LOCKED\"),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.IntField(a.ACTOR_ID),\n            FirstName: row.StringField(a.FIRST_NAME),\n            LastName:  row.StringField(a.LAST_NAME),\n        }\n    },\n)\n```\n\n**For Share**\n\n```sql\nSELECT a.actor_id, a.first_name, a.last_name\nFROM actor AS a\nWHERE a.first_name = 'DAN'\nFOR SHARE\n```\n\n```go\nactors, err := sq.FetchAll(db, sq.Postgres.\n    From(a).\n    Where(a.FIRST_NAME.EqString(\"DAN\")).\n    LockRows(\"FOR SHARE\"),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.IntField(a.ACTOR_ID),\n            FirstName: row.StringField(a.FIRST_NAME),\n            LastName:  row.StringField(a.LAST_NAME),\n        }\n    },\n)\n```\n\n#### RETURNING #postgres-returning\n\n```sql\nINSERT INTO actor\n    (first_name, last_name)\nVALUES\n    ('PENELOPE', 'GUINESS'),\n    ('NICK', 'WAHLBERG'),\n    ('ED', 'CHASE')\nRETURNING\n    actor.actor_id, actor.first_name, actor.last_name\n```\n\n```go\na := sq.New[ACTOR](\"\")\nactors, err := sq.FetchAll(db, sq.Postgres.\n    InsertInto(a).\n    Columns(a.FIRST_NAME, a.LAST_NAME).\n    Values(\"PENELOPE\", \"GUINESS\").\n    Values(\"NICK\", \"WAHLBERG\").\n    Values(\"ED\", \"CHASE\"),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.IntField(a.ACTOR_ID),\n            FirstName: row.StringField(a.FIRST_NAME),\n            LastName:  row.StringField(a.LAST_NAME),\n        }\n    },\n)\n```\n\n#### Insert ignore duplicates #postgres-insert-ignore-duplicates\n\n```sql\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (1, 'PENELOPE', 'GUINESS'),\n    (2, 'NICK', 'WAHLBERG'),\n    (3, 'ED', 'CHASE')\nON CONFLICT DO NOTHING\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.Postgres.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Values(1, \"PENELOPE\", \"GUINESS\").\n    Values(2, \"NICK\", \"WAHLBERG\").\n    Values(3, \"ED\", \"CHASE\").\n    OnConflict().DoNothing(),\n)\n```\n\n#### Upsert #postgres-upsert\n\n```sql\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (1, 'PENELOPE', 'GUINESS'),\n    (2, 'NICK', 'WAHLBERG'),\n    (3, 'ED', 'CHASE')\nON CONFLICT (actor_id) DO UPDATE SET\n    first_name = EXCLUDED.first_name,\n    last_name = EXCLUDED.last_name\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.Postgres.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Values(1, \"PENELOPE\", \"GUINESS\").\n    Values(2, \"NICK\", \"WAHLBERG\").\n    Values(3, \"ED\", \"CHASE\").\n    OnConflict(a.ACTOR_ID).DoUpdateSet(\n        a.FIRST_NAME.Set(a.FIRST_NAME.WithPrefix(\"EXCLUDED\")),\n        a.LAST_NAME.Set(a.LAST_NAME.WithPrefix(\"EXLCUDED\")),\n    ),\n)\n```\n\n#### Update with Join #postgres-update-with-join\n\n```sql\nUPDATE actor\nSET last_name = 'DINO'\nFROM film_actor\nJOIN film ON film.film_id = film_actor.film_id\nWHERE film_actor.actor_id = actor.actor_id AND film.title = 'ACADEMY DINOSAUR'\n```\n\n```go\na, fa, f := sq.New[ACTOR](\"\"), sq.New[FILM_ACTOR](\"\"), sq.New[FILM](\"\")\n_, err := sq.Exec(db, sq.Postgres.\n    Update(a).\n    Set(a.LAST_NAME.SetString(\"DINO\")).\n    From(fa).\n    Join(f, f.FILM_ID.Eq(fa.FILM_ID)).\n    Where(\n        fa.ACTOR_ID.Eq(a.ACTOR_ID),\n        f.TITLE.EqString(\"ACADEMY DINOSAUR\"),\n    ),\n)\n```\n\n#### Delete with Join #postgres-delete-with-join\n\n```sql\nDELETE FROM actor\nUSING film_actor\nJOIN film ON film.film_id = film_actor.film_id\nWHERE film_actor.actor_id = actor.actor_id AND film.title = 'ACADEMY DINOSAUR'\n```\n\n```go\na, fa, f := sq.New[ACTOR](\"\"), sq.New[FILM_ACTOR](\"\"), sq.New[FILM](\"\")\n_, err := sq.Exec(db, sq.Postgres.\n    DeleteFrom(a).\n    Using(fa).\n    Join(f, f.FILM_ID.Eq(fa.FILM_ID)).\n    Where(\n        fa.ACTOR_ID.Eq(a.ACTOR_ID),\n        f.TITLE.EqString(\"ACADEMY DINOSAUR\"),\n    ),\n)\n```\n\n#### Bulk Update #postgres-bulk-update\n\n```sql\nUPDATE actor\nSET\n    first_name = tmp.first_name,\n    last_name = tmp.last_name\nFROM (VALUES\n        (1, 'PENELOPE', 'GUINESS'),\n        (2, 'NICK', 'WAHLBERG'),\n        (3, 'ED', 'CHASE')\n    ) AS tmp (actor_id, first_name, last_name)\nWHERE tmp.actor_id = actor.actor_id\n```\n\n```go\na := sq.New[ACTOR](\"\")\ntmp := sq.TableValues{\n    Alias:     \"tmp\",\n    Columns:   []string{\"actor_id\", \"first_name\", \"last_name\"},\n    RowValues: [][]any{\n        {1, \"PENELOPE\", \"GUINESS\"},\n        {2, \"NICK\", \"WAHLBERG\"},\n        {3, \"ED\", \"CHASE\"},\n    },\n}\n_, err := sq.Exec(db, sq.Postgres.\n    Update(a).\n    Set(\n        a.FIRST_NAME.Set(tmp.Field(\"first_name\")),\n        a.LAST_NAME.Set(tmp.Field(\"last_name\")),\n    ).\n    From(tmp).\n    Where(tmp.Field(\"actor_id\").Eq(a.ACTOR_ID)),\n)\n```\n\n### MySQL-specific features #mysql-specific-features\n\n#### FOR UPDATE, FOR SHARE #mysql-for-update-for-share\n\n**For Update**\n\n```sql\nSELECT a.actor_id, a.first_name, a.last_name\nFROM actor AS a\nWHERE a.first_name = 'DAN'\nFOR UPDATE SKIP LOCKED\n```\n\n```go\nactors, err := sq.FetchAll(db, sq.MySQL.\n    From(a).\n    Where(a.FIRST_NAME.EqString(\"DAN\")).\n    LockRows(\"FOR UPDATE SKIP LOCKED\"),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.IntField(a.ACTOR_ID),\n            FirstName: row.StringField(a.FIRST_NAME),\n            LastName:  row.StringField(a.LAST_NAME),\n        }\n    },\n)\n```\n\n**For Share**\n\n```sql\nSELECT a.actor_id, a.first_name, a.last_name\nFROM actor AS a\nWHERE a.first_name = 'DAN'\nFOR SHARE\n```\n\n```go\nactors, err := sq.FetchAll(db, sq.MySQL.\n    From(a).\n    Where(a.FIRST_NAME.EqString(\"DAN\")).\n    LockRows(\"FOR SHARE\"),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.IntField(a.ACTOR_ID),\n            FirstName: row.StringField(a.FIRST_NAME),\n            LastName:  row.StringField(a.LAST_NAME),\n        }\n    },\n)\n```\n\n#### LastInsertId #mysql-last-insert-id\n\n```sql\nINSERT INTO actor (first_name, last_name) VALUES ('PENELOPE', 'GUINESS');\nSELECT last_insert_id();\n```\n\n```go\na := sq.New[ACTOR](\"\")\nresult, err := sq.Exec(db, sq.MySQL.\n    InsertInto(a).\n    Columns(a.FIRST_NAME, a.LAST_NAME).\n    Values(\"PENELOPE\", \"GUINESS\"),\n)\nif err != nil {\n}\nfmt.Println(result.LastInsertId) // int64\n```\n\n#### Insert ignore duplicates #mysql-insert-ignore-duplicates\n\n**ON DUPLICATE KEY UPDATE field = field**\n\nMySQL lacks ON DUPLICATE KEY DO NOTHING but assigning a field to itself is the closest thing we can get. If a field is assigned to itself, MySQL doesn't actually trigger an update (making it do nothing).\n\n```sql\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (1, 'PENELOPE', 'GUINESS'),\n    (2, 'NICK', 'WAHLBERG'),\n    (3, 'ED', 'CHASE')\nON DUPLICATE KEY UPDATE\n    actor.actor_id = actor.actor_id\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.MySQL.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Values(1, \"PENELOPE\", \"GUINESS\").\n    Values(2, \"NICK\", \"WAHLBERG\").\n    Values(3, \"ED\", \"CHASE\").\n    OnDuplicateKeyUpdate(\n        a.ACTOR_ID.Set(a.ACTOR_ID),\n    ),\n)\n```\n\n**INSERT IGNORE**\n\nINSERT IGNORE will ignore all kinds of errors (such as foreign key violations) so use only if you really, really don't care if an INSERT fails.\n\n```sql\nINSERT IGNORE INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (1, 'PENELOPE', 'GUINESS'),\n    (2, 'NICK', 'WAHLBERG'),\n    (3, 'ED', 'CHASE')\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.MySQL.\n    InsertIgnoreInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Values(1, \"PENELOPE\", \"GUINESS\").\n    Values(2, \"NICK\", \"WAHLBERG\").\n    Values(3, \"ED\", \"CHASE\"),\n)\n```\n\n#### Upsert #mysql-upsert\n\n**Row Alias (MySQL 8.0+ onwards)**\n\n```sql\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (1, 'PENELOPE', 'GUINESS'),\n    (2, 'NICK', 'WAHLBERG'),\n    (3, 'ED', 'CHASE')\nAS new\nON DUPLICATE KEY UPDATE\n    actor.first_name = new.first_name,\n    actor.last_name = new.last_name\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.MySQL.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Values(1, \"PENELOPE\", \"GUINESS\").\n    Values(2, \"NICK\", \"WAHLBERG\").\n    Values(3, \"ED\", \"CHASE\").\n    As(\"new\").\n    OnDuplicateKeyUpdate(\n        a.FIRST_NAME.Set(a.FIRST_NAME.WithPrefix(\"new\")),\n        a.LAST_NAME.Set(a.LAST_NAME.WithPrefix(\"new\")),\n    ),\n)\n```\n\n**VALUES()**\n\n```sql\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nVALUES\n    (1, 'PENELOPE', 'GUINESS'),\n    (2, 'NICK', 'WAHLBERG'),\n    (3, 'ED', 'CHASE')\nON DUPLICATE KEY UPDATE\n    actor.first_name = VALUES(first_name),\n    actor.last_name = VALUES(last_name)\n```\n\n```go\na := sq.New[ACTOR](\"\")\n_, err := sq.Exec(db, sq.MySQL.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Values(1, \"PENELOPE\", \"GUINESS\").\n    Values(2, \"NICK\", \"WAHLBERG\").\n    Values(3, \"ED\", \"CHASE\").\n    OnDuplicateKeyUpdate(\n        a.FIRST_NAME.Setf(\"VALUES({})\", a.FIRST_NAME.WithPrefix(\"\")),\n        a.LAST_NAME.Setf(\"VALUES({})\", a.LAST_NAME.WithPrefix(\"\")),\n    ),\n)\n```\n\n#### Update with Join #mysql-update-with-join\n\n```sql\nUPDATE actor\nJOIN film_actor ON film_actor.actor_id = actor.actor_id\nJOIN film ON film.film_id = film_actor.film_id\nSET actor.last_name = 'DINO'\nWHERE film.title = 'ACADEMY DINOSAUR'\n```\n\n```go\na, fa, f := sq.New[ACTOR](\"\"), sq.New[FILM_ACTOR](\"\"), sq.New[FILM](\"\")\n_, err := sq.Exec(db, sq.MySQL.\n    Update(a).\n    Join(fa, fa.ACTOR_ID.Eq(a.ACTOR_ID)).\n    Join(f, f.FILM_ID.Eq(fa.FILM_ID)).\n    Set(a.LAST_NAME.SetString(\"DINO\")).\n    Where(f.TITLE.EqString(\"ACADEMY DINOSAUR\")),\n)\n```\n\n#### Delete with Join #mysql-delete-with-join\n\n```sql\nDELETE actor\nFROM actor\nJOIN film_actor ON film_actor.actor_id = actor.actor_id\nJOIN film ON film.film_id = film_actor.film_id\nWHERE film.title = 'ACADEMY DINOSAUR'\n```\n\n```go\na, fa, f := sq.New[ACTOR](\"\"), sq.New[FILM_ACTOR](\"\"), sq.New[FILM](\"\")\n_, err := sq.Exec(db, sq.MySQL.\n    Delete(a).\n    From(a)\n    Join(fa, fa.ACTOR_ID.Eq(a.ACTOR_ID)).\n    Join(f, f.FILM_ID.Eq(fa.FILM_ID)).\n    Where(f.TITLE.EqString(\"ACADEMY DINOSAUR\")),\n)\n```\n\n#### Bulk Update #mysql-bulk-update\n\n```sql\nUPDATE actor\nJOIN (VALUES\n        ROW(1, 'PENELOPE', 'GUINESS'),\n        ROW(2, 'NICK', 'WAHLBERG'),\n        ROW(3, 'ED', 'CHASE')\n    ) AS tmp (actor_id, first_name, last_name) ON tmp.actor_id = actor.actor_id\nSET\n    first_name = tmp.first_name,\n    last_name = tmp.last_name\n```\n\n```go\na := sq.New[ACTOR](\"\")\ntmp := sq.TableValues{\n    Alias:     \"tmp\",\n    Columns:   []string{\"actor_id\", \"first_name\", \"last_name\"},\n    RowValues: [][]any{\n        {1, \"PENELOPE\", \"GUINESS\"},\n        {2, \"NICK\", \"WAHLBERG\"},\n        {3, \"ED\", \"CHASE\"},\n    },\n}\n_, err := sq.Exec(db, sq.MySQL.\n    Update(a).\n    Join(tmp, tmp.Field(\"actor_id\").Eq(a.ACTOR_ID)).\n    Set(\n        a.FIRST_NAME.Set(tmp.Field(\"first_name\")),\n        a.LAST_NAME.Set(tmp.Field(\"last_name\")),\n    ),\n)\n```\n\n### SQLServer-specific features #sqlserver-specific-features\n\n#### TOP, WITH TIES #sqlserver-top-with-ties\n\n```sql\nSELECT TOP 10 WITH TIES a.first_name\nFROM actor AS a\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nfirstNames, err := sq.FetchAll(db, sq.SQLServer.\n    From(a).\n    Top(10).WithTies(),\n    func(row *sq.Row) string {\n        return row.String(a.FIRST_NAME)\n    },\n)\n```\n\n#### OUTPUT #sqlserver-output\n\n```sql\nINSERT INTO actor\n    (first_name, last_name)\nOUTPUT\n    INSERTED.actor_id, INSERTED.first_name, INSERTED.last_name\nVALUES\n    ('PENELOPE', 'GUINESS'),\n    ('NICK', 'WAHLBERG'),\n    ('ED', 'CHASE')\n```\n\n```go\na := sq.New[ACTOR](\"\")\nactors, err := sq.FetchAll(db, sq.SQLServer.\n    InsertInto(a).\n    Columns(a.FIRST_NAME, a.LAST_NAME).\n    Values(\"PENELOPE\", \"GUINESS\").\n    Values(\"NICK\", \"WAHLBERG\").\n    Values(\"ED\", \"CHASE\"),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.IntField(a.ACTOR_ID),\n            FirstName: row.StringField(a.FIRST_NAME),\n            LastName:  row.StringField(a.LAST_NAME),\n        }\n    },\n)\n```\n\n**INSERTED.* vs DELETED.***\n\n- For Insert queries, OUTPUT fields to use the INSERTED.\\* prefix.\n- For Delete queries, OUTPUT fields use the DELETED.\\* prefix.\n- For Update queries, OUTPUT fields use the INSERTED.\\* prefix.\n\nTechnically both INSERTED.\\* and DELETED.\\* fields are supported for Update queries, but sq only supports INSERTED.\\* because that is how RETURNING behaves in SQLite and Postgres.\n\n#### Insert ignore duplicates #sqlserver-insert-ignore-duplicates\n\nThis is technically not an SQL Server-specific feature as SQL Server completely does not support this. You have to employ a workaround using INSERT with SELECT ([https://stackoverflow.com/a/10703792](https://stackoverflow.com/a/10703792)). I'm including the workaround here for completion's sake.\n\n```sql\n-- Insert rows that don't exist.\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nSELECT\n    actor_id, first_name, last_name\nFROM (\n    VALUES\n        (1, 'PENELOPE', 'GUINESS'),\n        (2, 'NICK', 'WAHLBERG'),\n        (3, 'ED', 'CHASE')\n    ) AS rowvalues (actor_id, first_name, last_name)\nWHERE NOT EXISTS (\n    SELECT 1 FROM actor WHERE actor.actor_id = rowvalues.actor_id\n)\n```\n\n```go\na := sq.New[ACTOR](\"\")\n// Insert rows that don't exist.\n_, err := sq.Exec(db, sq.SQLServer.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Select(sq.Queryf(\"SELECT actor_id, first_name, last_name\"+\n        \"FROM (VALUES {}) AS rowvalues (actor_id, first_name, last_name)\"+\n        \"WHERE NOT EXISTS (SELECT 1 FROM actor WHERE actor.actor_id = rowvalues.actor_id)\",\n        sq.RowValues{\n            {1, \"PENELOPE\", \"GUINESS\"},\n            {2, \"NICK\", \"WAHLBERG\"},\n            {3, \"ED\", \"CHASE\"},\n        },\n    )),\n)\n```\n\n#### Upsert #sqlserver-upsert\n\nThis is technically not an SQL Server-specific feature as SQL Server does not support this. You have to employ a 2-step workaround using an UPDATE with JOIN + an INSERT with SELECT ([https://sqlperformance.com/2020/09/locking/upsert-anti-pattern](https://sqlperformance.com/2020/09/locking/upsert-anti-pattern)). I'm including the workaround here for completion's sake.\n\nAvoid using MERGE for upserting.\n- [https://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/](https://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/)\n- [https://michaeljswart.com/2021/08/what-to-avoid-if-you-want-to-use-merge/](https://michaeljswart.com/2021/08/what-to-avoid-if-you-want-to-use-merge/)\n\n```sql\n-- Update rows that exist.\nUPDATE actor\nSET\n    first_name = rowvalues.first_name,\n    last_name = rowvalues.last_name\nFROM\n    actor\n    JOIN (VALUES\n        (1, 'PENELOPE', 'GUINESS'),\n        (2, 'NICK', 'WAHLBERG'),\n        (3, 'ED', 'CHASE')\n    ) AS rowvalues (actor_id, first_name, last_name) ON rowvalues.actor_id = actor.actor_id;\n\n-- Insert rows that don't exist.\nINSERT INTO actor\n    (actor_id, first_name, last_name)\nSELECT\n    actor_id, first_name, last_name\nFROM (VALUES\n        (1, 'PENELOPE', 'GUINESS'),\n        (2, 'NICK', 'WAHLBERG'),\n        (3, 'ED', 'CHASE')\n    ) AS rowvalues (actor_id, first_name, last_name)\nWHERE NOT EXISTS (\n    SELECT 1 FROM actor WHERE actor.actor_id = rowvalues.actor_id\n);\n```\n\n```go\na := sq.New[ACTOR](\"\")\n// Update rows that exist.\n_, err := sq.Exec(db, sq.SQLServer.\n    Update(a).\n    Set(\n        a.FIRST_NAME.Setf(\"rowvalues.first_name\"),\n        a.LAST_NAME.Setf(\"rowvalues.last_name\"),\n    ).\n    From(a).\n    Join(sq.\n        Queryf(\"VALUES {}\", sq.RowValues{\n            {1, \"PENELOPE\", \"GUINESS\"},\n            {2, \"NICK\", \"WAHLBERG\"},\n            {3, \"ED\", \"CHASE\"},\n        }).\n        As(\"rowvalues (actor_id, first_name, last_name)\"),\n        sq.Expr(\"rowvalues.actor_id\").Eq(a.ACTOR_ID),\n    )\n)\n\n// Insert rows that don't exist.\n_, err := sq.Exec(db, sq.SQLServer.\n    InsertInto(a).\n    Columns(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME).\n    Select(sq.Queryf(\"SELECT actor_id, first_name, last_name\"+\n        \"FROM (VALUES {}) AS rowvalues (actor_id, first_name, last_name)\"+\n        \"WHERE NOT EXISTS (SELECT 1 FROM actor WHERE actor.actor_id = rowvalues.actor_id)\",\n        sq.RowValues{\n            {1, \"PENELOPE\", \"GUINESS\"},\n            {2, \"NICK\", \"WAHLBERG\"},\n            {3, \"ED\", \"CHASE\"},\n        },\n    )),\n)\n```\n\n#### Update with Join #sqlserver-update-with-join\n\n```sql\nUPDATE actor\nSET last_name = 'DINO'\nFROM actor\nJOIN film_actor ON film_actor.actor_id = actor.actor_id\nJOIN film ON film.film_id = film_actor.film_id\nWHERE film.title = 'ACADEMY DINOSAUR'\n```\n\n```go\na, fa, f := sq.New[ACTOR](\"\"), sq.New[FILM_ACTOR](\"\"), sq.New[FILM](\"\")\n_, err := sq.Exec(db, sq.SQLServer.\n    Update(a).\n    Set(a.LAST_NAME.SetString(\"DINO\")).\n    From(a).\n    Join(fa, fa.ACTOR_ID.Eq(a.ACTOR_ID)).\n    Join(f, f.FILM_ID.Eq(fa.FILM_ID)).\n    Where(f.TITLE.EqString(\"ACADEMY DINOSAUR\")),\n)\n```\n\n#### Delete with Join #sqlserver-delete-with-join\n\n```sql\nDELETE actor\nFROM actor\nJOIN film_actor ON film_actor.actor_id = actor.actor_id\nJOIN film ON film.film_id = film_actor.film_id\nWHERE film.title = 'ACADEMY DINOSAUR'\n```\n\n```go\na, fa, f := sq.New[ACTOR](\"\"), sq.New[FILM_ACTOR](\"\"), sq.New[FILM](\"\")\n_, err := sq.Exec(db, sq.SQLServer.\n    Delete(a).\n    From(a)\n    Join(fa, fa.ACTOR_ID.Eq(a.ACTOR_ID)).\n    Join(f, f.FILM_ID.Eq(fa.FILM_ID)).\n    Where(f.TITLE.EqString(\"ACADEMY DINOSAUR\")),\n)\n```\n\n#### Bulk Update #sqlserver-bulk-update\n\n```sql\nUPDATE actor\nSET\n    first_name = tmp.first_name,\n    last_name = tmp.last_name\nFROM\n    actor\n    JOIN (VALUES\n        (1, 'PENELOPE', 'GUINESS'),\n        (2, 'NICK', 'WAHLBERG'),\n        (3, 'ED', 'CHASE')\n    ) AS tmp (actor_id, first_name, last_name) ON tmp.actor_id = actor.actor_id\n```\n\n```go\na := sq.New[ACTOR](\"\")\ntmp := sq.TableValues{\n    Alias:     \"tmp\",\n    Columns:   []string{\"actor_id\", \"first_name\", \"last_name\"},\n    RowValues: [][]any{\n        {1, \"PENELOPE\", \"GUINESS\"},\n        {2, \"NICK\", \"WAHLBERG\"},\n        {3, \"ED\", \"CHASE\"},\n    },\n}\n_, err := sq.Exec(db, sq.SQLServer.\n    Update(a).\n    Set(\n        a.FIRST_NAME.Set(tmp.Field(\"first_name\")),\n        a.LAST_NAME.Set(tmp.Field(\"last_name\")),\n    ).\n    From(a)\n    Join(tmp, tmp.Field(\"actor_id\").Eq(a.ACTOR_ID)),\n)\n```\n\n## Working with arrays, enums, JSON and UUID #arrays-enums-json-uuid\n\n### Arrays #arrays\n\nSlices of primitive types (`[]string`, `[]int64`, `[]int32`, `[]float64`, `[]float32`, `[]bool`) can be saved into the database. For Postgres, it will be saved as an ARRAY (TEXT[], INT[], BIGINT[], NUMERIC[] or BOOLEAN[]). For other databases, it will be saved as a JSON array.\n\n**Writing arrays**\n\n```go\n// Raw SQL\n_, err := sq.Exec(db, sq.\n    Queryf(\"INSERT INTO posts (title, body, tags) VALUES {}\", sq.RowValue{\n        \"Hello World!\",\n        \"This is my first blog post.\",\n        sq.ArrayValue([]string{\"introduction\", \"hello-world\", \"meta\"}),\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n\n// Query Builder\np := sq.New[POSTS](\"\")\n_, err := sq.Exec(db, sq.\n    InsertInto(p).\n    ColumnValues(func(col *sq.Column) {\n        col.SetString(p.TITLE, \"Hello World!\")\n        col.SetString(p.BODY, \"This is my first blog post.\")\n        col.SetArray(p.TAGS, []string{\"introduction\", \"hello-world\", \"meta\"})\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n**Reading arrays**\n\n```go\n// Raw SQL\nposts, err := sq.FetchAll(db, sq.\n    Queryf(\"SELECT {*} FROM posts WHERE post_id IN ({})\", []int{1, 2, 3}).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Post {\n        var post Post\n        post.Title = row.String(\"title\")\n        post.Body = row.String(\"body\")\n        row.Array(&post.Tags, \"tags\")\n        return post\n    },\n)\n\n// Query Builder\np := sq.New[POSTS](\"\")\nposts, err := sq.FetchAll(db, sq.\n    From(p).\n    Where(p.POST_ID.In([]int{1, 2, 3})).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Post {\n        var post Post\n        post.Title = row.StringField(p.TITLE)\n        post.Body = row.StringField(p.BODY)\n        row.ArrayField(&post.Tags, p.TAGS)\n        return post\n    },\n)\n```\n\n### Enums #enums\n\nA Go type is considered an enum if it implements the `Enumeration` interface:\n\n```go\ntype Enumeration interface{\n    Enumerate() []string\n}\n```\n\nAs an example, this is how an int-based enum and a string-based enum would be implemented:\n\n```go\ntype Color int\n\nconst (\n    ColorInvalid Color = iota\n    ColorRed\n    ColorGreen\n    ColorBlue\n)\n\nvar colorNames = [...]string{\n    ColorInvalid: \"\",\n    ColorRed:     \"red\",\n    ColorGreen:   \"green\",\n    ColorBlue:    \"blue\",\n}\n\nfunc (c Color) Enumerate() []string { return colorNames[:] }\n```\n\n```go\ntype Direction string\n\nconst (\n    DirectionInvalid = Direction(\"\")\n    DirectionNorth   = Direction(\"north\")\n    DirectionSouth   = Direction(\"south\")\n    DirectionEast    = Direction(\"east\")\n    DirectionWest    = Direction(\"west\")\n)\n\nfunc (d Direction) Enumerate() []string {\n    return []string{\n        string(DirectionInvalid),\n        string(DirectionNorth),\n        string(DirectionSouth),\n        string(DirectionEast),\n        string(DirectionWest),\n    }\n}\n```\n\nBy implementing the `Enumeration` interface, you automatically get enum type validation when writing enums to and reading enums from the database.\n- If you try to write an enum value to the database that isn't present in the `Enumerate()` slice, it will be flagged as an error.\n- If the database returns an enum value that isn't present in the `Enumerate()` slice, it will be flagged as an error.\n\n**Writing enums**\n\n```go\n// Raw SQL\n_, err := sq.Exec(db, sq.\n    Queryf(\"INSERT INTO fruits (name, color) VALUES {}\", sq.RowValue{\n        \"apple\",\n        sq.EnumValue(ColorRed),\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n\n// Query Builder\nf := sq.New[FRUITS](\"\")\n_, err := sq.Exec(db, sq.\n    InsertInto(f).\n    ColumnValues(func(col *sq.Column) {\n        col.SetString(f.NAME, \"apple\")\n        col.SetEnum(f.COLOR, ColorRed)\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n**Reading enums**\n\n```go\n// Raw SQL\nfruits, err := sq.FetchAll(db, sq.\n    Queryf(\"SELECT {*} FROM fruits WHERE fruit_id IN ({})\", []int{1, 2, 3}).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Fruit {\n        var fruit Fruit\n        fruit.Name = row.String(\"name\")\n        row.Enum(&fruit.Color, \"color\")\n        return fruit\n    },\n)\n\n// Query Builder\nf := sq.New[FRUITS](\"\")\nposts, err := sq.FetchAll(db, sq.\n    From(f).\n    Where(f.FRUIT_ID.In([]int{1, 2, 3})).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Fruit {\n        var fruit Fruit\n        fruit.Name = row.StringField(f.NAME)\n        row.EnumField(&fruit.Color, f.COLOR)\n        return fruit\n    },\n)\n```\n\n### JSON #json\n\nAny Go type that works with `json.Marshal` and `json.Unmarshal` can be saved into the database. For Postgres, it will be saved as JSONB. For MySQL, it will be saved as JSON. For other databases, it will be saved as a JSON string.\n\n**Writing JSON**\n\n```go\n// Raw SQL\n_, err := sq.Exec(db, sq.\n    Queryf(\"INSERT INTO products (name, price, attributes) VALUES {}\", sq.RowValue{\n        \"Sleeping Bag\",\n        89.99,\n        sq.JSONValue(map[string]any{\n            \"Length (cm)\":    220,\n            \"Width (cm)\":     150,\n            \"Weight (kg)\":    2.96,\n            \"Color\":          \"Lake Blue\",\n            \"Fill Material\":  \"190T Pongee\",\n            \"Outer Material\": \"Polyester\",\n        }),\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n\n// Query Builder\np := sq.New[PRODUCTS](\"\")\n_, err := sq.Exec(db, sq.\n    InsertInto(p).\n    ColumnValues(func(col *sq.Column) {\n        col.SetString(p.NAME, \"Sleeping Bag\")\n        col.SetFloat64(p.PRICE, 89.99)\n        col.SetJSON(p.ATTRIBUTES, map[string]any{\n            \"Length (cm)\":    220,\n            \"Width (cm)\":     150,\n            \"Weight (kg)\":    2.96,\n            \"Color\":          \"Lake Blue\",\n            \"Fill Material\":  \"190T Pongee\",\n            \"Outer Material\": \"Polyester\",\n        })\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n**Reading JSON**\n\n```go\n// Raw SQL\nproducts, err := sq.FetchAll(db, sq.\n    Queryf(\"SELECT {*} FROM products WHERE product_id IN ({})\", []int{1, 2, 3}).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Product {\n        var product Product\n        product.Name = row.String(\"name\")\n        product.Price = row.Float64(\"price\")\n        row.JSON(&product.Attributes, \"attributes\")\n        return product\n    },\n)\n\n// Query Builder\np := sq.New[PRODUCTS](\"\")\nposts, err := sq.FetchAll(db, sq.\n    From(p).\n    Where(p.PRODUCT_ID.In([]int{1, 2, 3})).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Product {\n        var product Product\n        product.Name = row.StringField(p.NAME)\n        product.Price = row.Float64Field(p.PRICE)\n        row.JSONField(&product.Attributes, p.ATTRIBUTES)\n        return product\n    },\n)\n```\n\n### UUID #uuid\n\nAny Go type whose underlying type is `[16]byte` can be saved as a UUID into the database. For Postgres, it will be saved as UUID. For other databases, it will be saved as a BINARY(16).\n\nIt is likely that the Go UUID library you are using already implements sql.Scanner and driver.Valuer (e.g. [github.com/google/uuid](https://github.com/google/uuid)). You can choose to rely on their built-in SQL behaviour:\n\n- Instead of wrapping the uuid in sq.UUIDValue(), just use the uuid directly.\n- Instead of calling col.SetUUID(), just call col.Set().\n- Instead of calling row.UUID()/row.UUIDField(), just call row.Scan()/row.ScanField().\n\nThe main benefit of using this library's built-in UUID helpers is to have UUID reading/writing work identically across database dialects: for Postgres, if you want to save a UUID you must give it a UUID string. For other databases, if you want to save a UUID as a BINARY(16) you must give it raw UUID bytes. Using this library's UUID helpers means you don't have to manually account for this UUID string/bytes disparity between Postgres and the other DBs.\n\n**Writing UUID**\n\n```go\nuserID, err := uuid.Parse(\"d619cde3-7661-4b6e-928e-4d5b239a18a9\")\nif err != nil {\n}\n\n// Raw SQL\n_, err = sq.Exec(db, sq.\n    Queryf(\"INSERT INTO users (user_id, name, email) VALUES {}\", sq.RowValue{\n        sq.UUIDValue(userID),\n        \"John Doe\",\n        \"john_doe@email.com\",\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n\n// Query Builder\nu := sq.New[USERS](\"\")\n_, err := sq.Exec(db, sq.\n    InsertInto(u).\n    ColumnValues(func(col *sq.Column) {\n        col.SetUUID(u.USER_ID, userID)\n        col.SetString(u.NAME, \"John Doe\")\n        col.SetString(u.EMAIL, \"john_doe@email.com\")\n    }).\n    SetDialect(sq.DialectPostgres),\n)\n```\n\n**Reading UUID**\n\n```go\n// Raw SQL\nusers, err := sq.FetchAll(db, sq.\n    Queryf(\"SELECT {*} FROM users WHERE email IS NOT NULL\").\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) User {\n        var user User\n        row.UUID(&user.UserID, \"user_id\")\n        user.Name = row.String(\"name\")\n        user.Email = row.String(\"email\")\n        return user\n    },\n)\n\n// Query Builder\nu := sq.New[USERS](\"\")\nposts, err := sq.FetchAll(db, sq.\n    From(u).\n    Where(u.EMAIL.IsNotNull()).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) User {\n        var user User\n        row.UUIDField(&user.UserID, u.USER_ID)\n        user.Name = row.StringField(u.NAME)\n        user.Email = row.StringField(u.EMAIL)\n        return user\n    },\n)\n```\n\n## Logging #logging\n\nQueries can be logged wrapping the database with `sq.Log()` or `sq.VerboseLog()`.\n\n**sq.Log()**\n\n```go\n// With logging                    ↓ wrap the db\nfirstName, err := sq.FetchOne(sq.Log(db), sq.\n    Queryf(\"SELECT {*} FROM actor WHERE last_name IN ({})\", []string{\"AKROYD\", \"ALLEN\", \"WILLIAMS\"}),\n    func(row *sq.Row) string {\n        return row.String(\"first_name\")\n    },\n)\n```\n\n```shell\n2022/02/06 15:34:36 [OK] SELECT first_name FROM actor WHERE last_name IN (?, ?, ?) | timeTaken=9.834µs rowCount=9 caller=/Users/bokwoon/Documents/sq/fetch_exec_test.go:74:sq.TestFetchExec\n```\n\n**sq.VerboseLog()**\n\n```go\n// With verbose logging                ↓ wrap the db\nfirstName, err := sq.FetchOne(sq.VerboseLog(db), sq.\n    Queryf(\"SELECT {*} FROM actor WHERE last_name IN ({})\", []string{\"AKROYD\", \"ALLEN\", \"WILLIAMS\"}),\n    func(row *sq.Row) string {\n        return row.String(\"first_name\")\n    },\n)\n```\n\n```shell\n2022/02/06 15:34:36 [OK] timeTaken=9.834µs rowCount=9 caller=/Users/bokwoon/Documents/sq/fetch_exec_test.go:74:sq.TestFetchExec\n----[ Executing query ]----\nSELECT first_name FROM actor WHERE last_name IN (?, ?, ?) []interface {}{\"AKROYD\", \"ALLEN\", \"WILLIAMS\"}\n----[ with bind values ]----\nSELECT first_name FROM actor WHERE last_name IN ('AKROYD', 'ALLEN', 'WILLIAMS')\n----[ Fetched result ]----\n----[ Row 1 ]----\nfirst_name: 'CHRISTIAN'\n----[ Row 2 ]----\nfirst_name: 'SEAN'\n----[ Row 3 ]----\nfirst_name: 'KIRSTEN'\n----[ Row 4 ]----\nfirst_name: 'CUBA'\n----[ Row 5 ]----\nfirst_name: 'MORGAN'\n...\n(Fetched 9 rows)\n```\n\n### Logging without manual sq.Log() wrapping #logging-without-manual-wrapping\n\nTo log every query without manually wrapping it in sq.Log(), set the global logger using SetDefaultLogQuery(). It takes in a callback function which is called everytime a query is called (if no logger was explicitly provided to FetchOne, FetchAll, Exec, etc).\n\n```go\nfunc init() {\n    logger := sq.NewLogger(os.Stdout, \"\", log.LstdFlags, sq.LoggerConfig{\n        ShowTimeTaken: true,\n        HideArgs:      true,\n    })\n    sq.SetDefaultLogQuery(func(ctx context.Context, queryStats sq.QueryStats) {\n        // You can choose to only log queries if they encountered an error.\n        // if queryStats.Err == nil {\n        //     return\n        // }\n        logger.SqLogQuery(ctx, queryStats)\n    })\n}\n```\n\n### Custom logger #custom-logger\n\nA custom logger can also be used by creating [custom DB type that implements the `SqLogger` interface](#logging-without-manual-wrapping). The logging information is passed in as a `QueryStats` struct, which you can feed into the structured logger of your choice.\n\n```go\n// QueryStats represents the statistics from running a query.\ntype QueryStats struct {\n    // Dialect of the query.\n    Dialect string\n\n    // Query string.\n    Query string\n\n    // Args slice provided with the query string.\n    Args []any\n\n    // Params maps param names back to arguments in the args slice (by index).\n    Params map[string][]int\n\n    // Err is the error from running the query.\n    Err error\n\n    // RowCount from running the query. Not valid for Exec().\n    RowCount sql.NullInt64\n\n    // RowsAffected by running the query. Not valid for\n    // FetchOne/FetchAll/FetchCursor.\n    RowsAffected sql.NullInt64\n\n    // LastInsertId of the query.\n    LastInsertId sql.NullInt64\n\n    // Exists is the result of FetchExists().\n    Exists sql.NullBool\n\n    // When the query started at.\n    StartedAt time.Time\n\n    // Time taken by the query.\n    TimeTaken time.Duration\n\n    // The caller file where the query was invoked.\n    CallerFile string\n\n    // The line in the caller file that invoked the query.\n    CallerLine int\n\n    // The name of the function where the query was invoked.\n    CallerFunction string\n\n    // The results from running the query (if it was provided).\n    Results string\n}\n```\n\nAs an example, we will create a custom database logger that outputs JSON and only logs if the query took longer than 1 second.\n\n```go\ntype MyDB struct {\n    *sql.DB\n}\n\nfunc (myDB MyDB) SqLogSettings(ctx context.Context, settings *sq.LogSettings) {\n    settings.LogAsynchronously = false // Should the logging be dispatched in a separate goroutine?\n    settings.IncludeTime = true        // Should timeTaken be included in the QueryStats?\n    settings.IncludeCaller = true      // Should caller info be included in the QueryStats?\n    settings.IncludeResults = 0        // The first how many rows of results should be included? Leave 0 to not include any results.\n}\n\nfunc (myDB MyDB) SqLogQuery(ctx context.Context, stats sq.QueryStats) {\n    if stats.TimeTaken < time.Second {\n        return\n    }\n    output := map[string]any{\n        \"query\":     stats.Query,\n        \"args\":      stats.Args,\n        \"caller\":    stats.CallerFile + \":\" + strconv.Itoa(stats.CallerLine)\n        \"timeTaken\": stats.TimeTaken.String(),\n    }\n    b, err := json.MarshalIndent(output, \"\", \"  \")\n    if err != nil {\n        log.Println(err.Error())\n        return\n    }\n    log.Println(\"TOO SLOW! \" + string(b))\n}\n```\n\n```shell\n2022/02/06 15:34:36 TOO SLOW! {\n  \"args\": [\n    1\n  ],\n  \"caller\": \"/Users/bokwoon/Documents/sq/fetch_exec_test.go:74\",\n  \"query\": \"SELECT actor_id, first_name, last_name FROM actor WHERE actor_id = ?\",\n  \"timeTaken\": \"1.534s\"\n}\n```\n\n## Working with transactions #transactions\n\nFetch() and Exec() both accept an sq.DB interface, which represents something that can query the database.\n\n\n```go\n// *sql.Conn, *sql.DB and *sql.Tx all implement DB.\ntype DB interface {\n    QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)\n    ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)\n    PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)\n}\n```\n\nTo use an \\*sql.Tx (or an \\*sql.Conn), you can pass it in like a normal \\*sql.DB.\n\n```go\ntx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})\nif err != nil {\n    return err\n}\n// good practice defer tx.Rollback first, if tx.Commit is called then this becomes a no-op.\ndefer tx.Rollback()\n\n// do operation 1\n_, err = sq.Exec(tx, q1)\nif err != nil {\n    return err\n}\n\n// do operation 2\n_, err = sq.Exec(tx, q2)\nif err != nil {\n    return err\n}\n\n// do operation 3\n_, err = sq.Exec(tx, q3)\nif err != nil {\n    return err\n}\n\n// If all goes well, commit. If anything wrong happened before reaching here we\n// just bail and let defer tx.Rollback() kick in\nerr = tx.Commit()\nif err != nil {\n    return err\n}\n// if we reach here, success\n```\n\n## Compiling queries #compiling-queries\n\nThe cost of query building can be amortized by compiling queries down into a query string and args slice. Compiled queries are reused by supplying a different set of parameters each time you execute them. They can be executed safely in parallel.\n\n```go\n// Compile the query.\ncompiledQuery, err := sq.CompileFetch(sq.\n    Queryf(\"SELECT {*} FROM actor WHERE first_name = {first_name}, last_name = {last_name}\",\n        sql.Named(\"first_name\", nil), // first_name is a rebindable param, with default value nil\n        sql.Named(\"last_name\", nil),  // last_name is a rebindable param, with default value nil\n    ).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.Int(\"actor_id\"),\n            FirstName: row.String(\"first_name\"),\n            LastName:  row.String(\"last_name\"),\n        }\n    },\n)\nif err != nil {\n}\n\n// Obtain the query string and args slice back from the CompiledFetch.\n// The params map and rowmapper function are also available.\nquery, args, params, rowmapper := compiledQuery.GetSQL()\n\n// Execute the compiled query with the default values.\nactor, err := compiledQuery.FetchOne(db, nil)\nif err != nil {\n}\n\n// Execute the compiled query with values first_name = \"DAN\", last_name = \"TORN\".\nactor, err := compiledQuery.FetchOne(db, sq.Params{\n    \"first_name\": \"DAN\",\n    \"last_name\":  \"TORN\",\n})\nif err != nil {\n}\n```\n\n### Rebindable params #rebindable-params\n\nOnly [named parameters](#ordinal-named-placeholders) can be rebinded in a compiled query, which means they must be provided during the query building phase.\n\n```go\n// WRONG: actor_id cannot be rebinded.\ncompiledQuery, err := sq.CompileFetch(\n    sq.Queryf(\"SELECT {*} FROM actor WHERE actor_id = {}\", 1),\n    func(row *sq.Row) Actor {\n        return Actor{\n            FirstName: row.String(\"first_name\"),\n            LastName:  row.String(\"last_name\"),\n        }\n    },\n)\nif err != nil {\n}\n// ERROR: named parameter {actorID} not provided\nactor, err := compiledQuery.FetchOne(db, sq.Params{\"actorID\": 2})\nif err != nil {\n}\n\n// CORRECT: actor_id can be rebinded (using \"actorID\").\ncompiledQuery, err := sq.CompileFetch(\n    sq.Queryf(\"SELECT {*} FROM actor WHERE actor_id = {actorID}\", sql.Named(\"actorID\", 1)),\n    func(row *sq.Row) Actor {\n        return Actor{\n            FirstName: row.String(\"first_name\"),\n            LastName:  row.String(\"last_name\"),\n        }\n    },\n)\nif err != nil {\n}\nactor, err := compiledQuery.FetchOne(db, sq.Params{\"actorID\": 2})\nif err != nil {\n}\n```\n\nMost of the time you should use [sql.Named()](https://pkg.go.dev/database/sql#Named), but if you need to conform to various interfaces like [String](https://pkg.go.dev/github.com/bokwoon95/sq#String) or [Number](https://pkg.go.dev/github.com/bokwoon95/sq#Number) you can use the typed versions [sq.StringParam()](https://pkg.go.dev/github.com/bokwoon95/sq#StringParam) or [sq.IntParam()](https://pkg.go.dev/github.com/bokwoon95/sq#IntParam).\n\n<div class=\"table-wrapper\">\n<table>\n<thead>\n<tr>\n    <th>Parameter</th>\n    <th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n    <td>sql.Named(<code>name string</code>, <code>value any</code>)</td>\n    <td>database/sql's named parameter type</td>\n</tr>\n<tr>\n    <td>sq.Param(<code>name string</code>, <code>value any</code>)</td>\n    <td>same as sql.Named, but satisfies the <code>Field</code> interface</td>\n</tr>\n<tr>\n    <td>sq.BinaryParam(<code>name string</code>, <code>b []byte</code>)</td>\n    <td>same as sql.Named, but satisfies the <code>Binary</code> interface</td>\n</tr>\n<tr>\n    <td>sq.BooleanParam(<code>name string</code>, <code>b bool</code>)</td>\n    <td>same as sql.Named, but satisfies the <code>Boolean</code> interface</td>\n</tr>\n<tr>\n    <td>sq.IntParam(<code>name string</code>, <code>num int</code>)</td>\n    <td>same as sql.Named, but satisfies the <code>Number</code> interface</td>\n</tr>\n<tr>\n    <td>sq.Int64Param(<code>name string</code>, <code>num int64</code>)</td>\n    <td>same as sql.Named, but satisfies the <code>Number</code> interface</td>\n</tr>\n<tr>\n    <td>sq.Float64Param(<code>name string</code>, <code>num float64</code>)</td>\n    <td>same as sql.Named, but satisfies the <code>Number</code> interface</td>\n</tr>\n<tr>\n    <td>sq.StringParam(<code>name string</code>, <code>s string</code>)</td>\n    <td>same as sql.Named, but satisfies the <code>String</code> interface</td>\n</tr>\n<tr>\n    <td>sq.TimeParam(<code>name string</code>, <code>t time.Time</code>)</td>\n    <td>same as sql.Named, but satisfies the <code>Time</code> interface</td>\n</tr>\n</tbody>\n</table>\n</div>\n\n### CompiledFetch example #compiled-fetch\n```go\ntype ACTOR struct {\n    sq.TableStruct\n    ACTOR_ID    sq.NumberField\n    FIRST_NAME  sq.StringField\n    LAST_NAME   sq.StringField\n    LAST_UPDATE sq.TimeField\n}\n\na := sq.New[ACTOR](\"\")\ncompiledQuery, err := sq.CompileFetch(sq.\n    From(a).\n    Where(a.ACTOR_ID.Eq(sq.IntParam(\"actor_id\", 0))). // actor_id is a rebindable param, with default value 0\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.IntField(a.ACTOR_ID),\n            FirstName: row.StringField(a.FIRST_NAME),\n            LastName:  row.StringField(a.LAST_NAME),\n        }\n    },\n)\nif err != nil {\n}\n\nactor, err := compiledQuery.FetchOne(db, sq.Params{\"actor_id\": 1})\nfmt.Println(actor) // {ActorID: 1, FirstName: \"PENELOPE\", LastName: \"GUINESS\"}\n\nactor, err = compiledQuery.FetchOne(db, sq.Params{\"actor_id\": 2})\nfmt.Println(actor) // {ActorID: 2, FirstName: \"NICK\", LastName: \"WAHLBERG\"}\n\nactor, err = compiledQuery.FetchOne(db, sq.Params{\"actor_id\": 3})\nfmt.Println(actor) // {ActorID: 3, FirstName: \"ED\", LastName: \"CHASE\"}\n```\n\n### CompiledExec example #compiled-exec\n```go\ntype ACTOR struct {\n    sq.TableStruct\n    ACTOR_ID    sq.NumberField\n    FIRST_NAME  sq.StringField\n    LAST_NAME   sq.StringField\n    LAST_UPDATE sq.TimeField\n}\n\na := sq.New[ACTOR](\"\")\ncompiledQuery, err = sq.CompileExec(sq.\n    InsertInto(a).\n    ColumnValues(func(col *sq.Column) {\n        col.Set(a.ACTOR_ID, sql.Named(\"actor_id\", nil))     // actor_id is a rebindable param, with default value nil\n        col.Set(a.FIRST_NAME, sql.Named(\"first_name\", nil)) // first_name is a rebindable param, with default value nil\n        col.Set(a.LAST_NAME, sql.Named(\"last_name\", nil))   // last_name is a rebindable param, with default value nil\n        return nil\n    }).\n    SetDialect(sq.DialectPostgres),\n)\nif err != nil {\n}\n\n_, err := compiledQuery.Exec(db, sq.Params{\n    \"actor_id\":   1,\n    \"first_name\": \"PENELOPE\",\n    \"last_name\":  \"GUINESS\",\n})\n// INSERT INTO actor (actor_id, first_name, last_name) VALUES (1, 'PENELOPE', 'GUINESS')\n\n_, err = compiledQuery.Exec(db, sq.Params{\n    \"actor_id\":   2,\n    \"first_name\": \"NICK\",\n    \"last_name\":  \"WAHLBERG\",\n})\n// INSERT INTO actor (actor_id, first_name, last_name) VALUES (2, 'NICK', 'WAHLBERG')\n\n_, err = compiledQuery.Exec(db, sq.Params{\n    \"actor_id\":   3,\n    \"first_name\": \"ED\",\n    \"last_name\":  \"CHASE\",\n})\n// INSERT INTO actor (actor_id, first_name, last_name) VALUES (3, 'ED', 'CHASE')\n```\n\n### Preparing queries #preparing-queries\n\n[Compiled queries](#compiling-queries) can be further prepared by binding it to a database connection (creating a prepared statement).\n\n```go\ntype ACTOR struct {\n    sq.TableStruct\n    ACTOR_ID    sq.NumberField\n    FIRST_NAME  sq.StringField\n    LAST_NAME   sq.StringField\n    LAST_UPDATE sq.TimeField\n}\n\n// Compile the query.\na := sq.New[ACTOR](\"\")\ncompiledQuery, err := sq.CompileFetch(sq.\n    From(a).\n    Where(a.ACTOR_ID.Eq(sq.IntParam(\"actor_id\", 0))).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.IntField(a.ACTOR_ID),\n            FirstName: row.StringField(a.FIRST_NAME),\n            LastName:  row.StringField(a.LAST_NAME),\n        }\n    },\n)\nif err != nil {\n}\n\n// Prepare the compiled query.\npreparedQuery, err := compiledQuery.Prepare(db)\nif err != nil {\n}\n\n// Use the prepared query with default values.\nactor, err := preparedQuery.FetchOne(nil)\nif err != nil {\n}\n\n// Use the prepared query with values actor_id = 1.\nactor, err = preparedQuery.FetchOne(sq.Params{\"actor_id\": 1})\nif err != nil {\n}\n```\n\nAlternatively, you may directly prepare PreparedQuery directly with PrepareFetch.\n\n```go\n// Prepare the query.\npreparedQuery, err := sq.PrepareFetch(db, sq.\n    Queryf(\"SELECT {*} FROM actor WHERE first_name = {first_name}, last_name = {last_name}\",\n        sql.Named(\"first_name\", nil), // first_name is a rebindable param, with default value nil\n        sql.Named(\"last_name\", nil),  // last_name is a rebindable param, with default value nil\n    ).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Actor {\n        return Actor{\n            ActorID:   row.Int(\"actor_id\"),\n            FirstName: row.String(\"first_name\"),\n            LastName:  row.String(\"last_name\"),\n        }\n    },\n)\nif err != nil {\n}\n\n// Obtain a CompiledFetch from the PreparedFetch. This is useful if you need to\n// re-prepare the query on another DB connection.\ncompiledQuery := preparedQuery.GetCompiled()\n// Example:\npreparedQuery, err = compiledQuery.Prepare(db2)\n\n// Execute the prepared query with the default values.\nactor, err := preparedQuery.FetchOne(nil)\nif err != nil {\n}\n\n// Execute the prepared query with values first_name = \"DAN\", last_name = \"TORN\".\nactor, err := preparedQuery.FetchOne(sq.Params{\n    \"first_name\": \"DAN\",\n    \"last_name\":  \"TORN\",\n})\nif err != nil {\n}\n```\n\n## Application-side Row Level Security #appliction-side-row-level-security\n\nYou can define policies on your table structs such that whenever it is used in a query, it will produce an additional predicate to be added to the query. This roughly emulates Postgres' Row Level Security, except it works completely application-side and supports every database (not just Postgres).\n\nSince table policies are baked directly into the query string, it plays well with database/sql's connection pooling because you don't have to set session-level variables (which force you to use an \\*sql.Tx or \\*sql.Conn). That means it also plays well with an external connection pooler like PgBouncer, because again no session-level variables are required.\n\nThe main downside is that this can be easily bypassed if you reference the table directly with raw SQL instead of using the query builder.\n\n### A PolicyTable example #policytable\n\nTo define a table policy, a table struct must implement the `PolicyTable` interface.\n\n```go\ntype PolicyTable interface {\n    Table\n    Policy(ctx context.Context, dialect string) (Predicate, error)\n}\n```\n\nThe context is the same context that was passed in to **sq.FetchAllContext**, **sq.FetchOneContext** or **sq.ExecContext**.\n\nAs an example, we will define a table `employees` that stores employees for multiple tenants (indicated by the `tenant_id`). Any SELECT, UPDATE or DELETE query that hits the `employees` table must have a `tenant_id` predicate added to it.\n\n**Before**\n```sql\nSELECT name FROM employees;\nUPDATE employees SET name = $1 WHERE employee_id = $2;\nDELETE FROM employees WHERE employee_id = $1;\n```\n\n**After**\n```sql\nSELECT name FROM employees WHERE tenant_id = $1;\nUPDATE employees SET name = $1 WHERE tenant_id = $2 AND employee_id = $3;\nDELETE FROM employees WHERE tenant_id = $1 AND employee_id = $2;\n```\n\nHere is how to define the policy on the employees table.\n\n```go\ntype EMPLOYEES struct {\n    sq.TableStruct\n    TENANT_ID   sq.NumberField\n    EMPLOYEE_ID sq.NumberField\n    NAME        sq.StringField\n}\n\nfunc (tbl EMPLOYEES) Policy(ctx context.Context, dialect string) (sq.Predicate, error) {\n    tenantID, ok := ctx.Value(\"tenantID\").(int)\n    if !ok {\n        return nil, errors.New(\"tenantID not provided\")\n    }\n    return tbl.TENANT_ID.EqInt(tenantID), nil\n}\n```\nNote that if the `tenantID` cannot be retrieved from the context, `(EMPLOYEES).Policy()` returns an error. This means that any invocation of the `EMPLOYEES` table struct will always require the `tenantID` to be in the context or else query building will fail. You may choose to omit this check by simply returning a `nil` Predicate. `nil` Predicates do not get added to the query.\n\nHere is how to use employees table.\n```go\n// get tenantID from somewhere and put it into the context\nctx := context.WithValue(context.Background(), \"tenantID\", 1)\ne := sq.New[EMPLOYEES](\"\")\n\n// Query 1\nnames, err := sq.FetchAllContext(ctx, db, sq.From(e),\n    func(row *sq.Row) string {\n        return row.String(e.NAME)\n    },\n)\n// SELECT employees.name FROM employees WHERE employees.tenant_id = 1\n\n// Query 2\n_, err := sq.ExecContext(ctx, db, sq.\n    Update(e).\n    Set(e.NAME.SetString(\"BOB\")).\n    Where(e.EMPLOYEE_ID.EqInt(18)),\n)\n// UPDATE employees SET name = 'BOB' WHERE employees.tenant_id = 1 AND employees.employee_id = 18\n\n// Query 3\n_, err := sq.ExecContext(ctx, db, sq.\n    DeleteFrom(e).\n    Where(e.EMPLOYEE_ID.EqInt(18)),\n)\n// DELETE FROM employees WHERE employees.tenant_id = 1 AND employees.employee_id = 18\n```\n\n## SQL examples #sql-examples\n\n### IN #in\n\n#### In slice #in-slice\n\n```sql\na.actor_id IN (1, 2, 3)\n```\n\n```go\na := sq.New[ACTOR](\"a\")\na.ACTOR_ID.In([]int{1, 2, 3})\n```\n\n#### In RowValues #in-rowvalues\n\n```sql\na.first_name IN ('PENELOPE', 'NICK', 'ED')\n(a.first_name, a.last_name) IN (('PENELOPE', 'GUINESS'), ('NICK', 'WAHLBERG'), ('ED', 'CHASE'))\n```\n\n```go\na := sq.New[ACTOR](\"a\")\na.FIRST_NAME.In(sq.RowValue{\"PENELOPE\", \"NICK\", \"ED\"})\nsq.RowValue{a.FIRST_NAME, a.LAST_NAME}.In(sq.RowValues{\n    {\"PENELOPE\", \"GUINESS\"},\n    {\"NICK\", \"WAHLBERG\"},\n    {\"ED\", \"CHASE\"},\n})\n```\n\n#### In Subquery #in-subquery\n\n```sql\n(actor.first_name, actor.last_name) IN (\n    SELECT a.first_name, a.last_name\n    FROM actor AS a\n    WHERE a.actor_id <= 3\n)\n```\n\n```go\nactor, a := sq.New[ACTOR](\"\"), sq.New[ACTOR](\"a\")\nsq.RowValue{actor.FIRST_NAME, actor.LAST_NAME}.In(sq.\n    Select(a.FIRST_NAME, a.LAST_NAME).\n    From(a).\n    Where(a.ACTOR_ID.Le(3)),\n)\n```\n\n### CASE #case\n\n#### Predicate Case #predicate-case\n\n```sql\nCASE\n    WHEN f.length <= 60 THEN 'short'\n    WHEN f.length > 60 AND f.length <= 120 THEN 'medium'\n    ELSE 'long'\nEND AS length_type\n```\n\n```go\nf := sq.New[FILM](\"f\")\nsq.CaseWhen(f.LENGTH.LeInt(60), \"short\").\n    CaseWhen(sq.And(f.LENGTH.GtInt(60), f.LENGTH.LeInt(120)), \"medium\").\n    Else(\"long\").\n    As(\"length_type\")\n```\n\n#### Simple Case #simple-case\n\n```sql\nCASE f.rating\n    WHEN 'G' THEN 'family'\n    WHEN 'PG' THEN 'teens'\n    WHEN 'PG-13' THEN 'teens'\n    WHEN 'R' THEN 'adults'\n    WHEN 'NC-17' THEN 'adults'\n    ELSE 'unknown'\nEND AS audience\n```\n\n```go\nf := sq.New[FILM](\"f\")\nsq.Case(f.RATING).\n    When(\"G\", \"family\").\n    When(\"PG\", \"teens\").\n    When(\"PG-13\", \"teens\").\n    When(\"R\", \"adults\").\n    When(\"NC-17\", \"adults\").\n    Else(\"unknown\").\n    As(\"Audience\")\n```\n\n### EXISTS #exists\n\n#### Where Exists #where-exists\n\n```sql\nSELECT c.customer_id, c.first_name, c.last_name\nFROM customers AS c\nWHERE EXISTS (\n    SELECT 1\n    FROM orders AS o\n    WHERE o.customer_id = c.customer_id\n    GROUP BY o.customer_id\n    HAVING COUNT(*) > 2\n)\nORDER BY c.first_name, c.last_name\n```\n\n```go\nc, o := sq.New[CUSTOMERS](\"c\"), sq.New[ORDERS](\"o\")\ncustomers, err := sq.FetchAll(db, sq.\n    From(c).\n    Where(sq.Exists(sq.\n        SelectOne().\n        From(o).\n        Where(o.CUSTOMER_ID.Eq(c.CUSTOMER_ID)).\n        GroupBy(o.CUSTOMER_ID).\n        Having(sq.Expr(\"COUNT(*) > 2\")),\n    )).\n    OrderBy(c.FIRST_NAME, c.LAST_NAME),\n    func(row *sq.Row) Customer {\n        return Customer{\n            CustomerID: row.Int(c.CUSTOMER_ID),\n            FirstName:  row.String(c.FIRST_NAME),\n            LastName:   row.String(c.LAST_NAME),\n        }\n    },\n)\n```\n\n#### Where Not Exists #where-not-exists\n\n```sql\nSELECT p.product_id, p.product_name\nFROM products AS p\nWHERE NOT EXISTS (\n    SELECT 1\n    FROM order_details AS od\n    WHERE p.product_id = od.product_id\n)\n```\n\n```go\np, od := sq.New[PRODUCTS](\"p\"), sq.New[ORDER_DETAILS](\"od\")\nproducts, err := sq.FetchAll(db, sq.\n    From(p).\n    Where(sq.NotExists(sq.\n        SelectOne().\n        From(od).\n        Where(p.PRODUCT_ID.Eq(od.PRODUCT_ID)),\n    )),\n    func(row *sq.Row) Product {\n        return Product{\n            ProductID:   row.Int(p.PRODUCT_ID),\n            ProductName: row.String(p.PRODUCT_NAME),\n        }\n    },\n)\n```\n\n### Subqueries #subqueries\n\nA Subquery is a SelectQuery nested inside another SelectQuery.\n\n**Using SelectQuery as Field**\n\n```sql\nSELECT\n    city.city,\n    (SELECT country.country\n        FROM country\n        WHERE country.country_id = city.country_id) AS country\nFROM\n    city\nWHERE\n    city.city = 'Vancouver'\n```\n\n```go\ncity, country := sq.New[CITY](\"\"), sq.New[COUNTRY](\"\")\nresults, err := sq.FetchAll(db, sq.\n    From(city).\n    Where(city.CITY.EqString(\"Vancouver\")).\n    SetDialect(sq.DialectPostgres),\n    func(row *sq.Row) Result {\n        return Result{\n            City:    row.StringField(city.CITY),\n            Country: row.StringField(sq.\n                Select(country.COUNTRY).\n                From(country).\n                Where(country.COUNTRY_ID.Eq(city.COUNTRY_ID)).\n                As(\"country\"),\n            ),\n        }\n    },\n)\n```\n\n**Using SelectQuery as Table**\n\n```sql\nSELECT\n    film.title,\n    film_stats.actor_count\nFROM\n    film\n    JOIN (\n        SELECT film_actor.film_id, COUNT(*) AS actor_count\n        FROM film_actor\n        GROUP BY film_actor.film_id\n    ) AS film_stats ON film_stats.film_id = film.film_id\n```\n\n```go\nfilm, film_actor := sq.New[FILM](\"\"), sq.New[FILM_ACTOR](\"\")\n// create the subquery\nfilm_stats := sq.Postgres.\n    Select(\n        film_actor.FILM_ID,\n        sq.CountStar().As(\"actor_count\"),\n    ).\n    From(film_actor).\n    GroupBy(film_actor.FILM_ID).\n    As(\"film_stats\")\n// use the subquery\nresults, err := sq.FetchAll(db, sq.\n    From(film).\n    Join(film_stats, film_stats.Field(\"field_id\").Eq(film.FILM_ID)),\n    func(row *sq.Row) Result {\n        return Result{\n            Title:      row.String(film.TITLE),\n            ActorCount: row.Int(film_stats.Field(\"actor_count\")),\n        }\n    },\n)\n```\n\n### WITH (Common Table Expressions) #common-table-expressions\n\nCommon Table Expressions (CTEs) are an alternative to [subqueries](#subqueries).\n\n```sql\nWITH film_stats AS (\n    SELECT film_id, COUNT(*) AS actor_count\n    FROM film_actor\n    GROUP BY film_id\n)\nSELECT\n    film.title,\n    film_stats.actor_count\nFROM\n    film\n    JOIN film_stats ON film_stats.film_id = film.film_id\n```\n\n```go\nfilm, film_actor := sq.New[FILM](\"\"), sq.New[FILM_ACTOR](\"\")\n// create the CTE\nfilm_stats := sq.NewCTE(\"film_stats\", nil, sq.Postgres.\n    Select(\n        film_actor.FILM_ID,\n        sq.CountStar().As(\"actor_count\"),\n    ).\n    From(film_actor).\n    GroupBy(film_actor.FILM_ID),\n)\n// use the CTE\nresults, err := sq.FetchAll(db, sq.Postgres.\n    With(film_stats).\n    From(film).\n    Join(film_stats, film_stats.Field(\"field_id\").Eq(film.FILM_ID)),\n    func(row *sq.Row) Result {\n        return Result{\n            Title:      row.String(film.TITLE),\n            ActorCount: row.Int(film_stats.Field(\"actor_count\")),\n        }\n    },\n)\n```\n\n**Recursive Common Table Expressions**\n\n```sql\nWITH RECURSIVE counter (n) AS (\n    SELECT 1\n    UNION ALL\n    SELECT counter.n + 1 FROM counter WHERE counter.n + 1 <= 100\n)\nSELECT counter.n FROM counter;\n```\n\n```go\ncounter := sq.NewRecursiveCTE(\"counter\", []string{\"n\"}, sq.UnionAll(\n    sq.Queryf(\"SELECT 1\"),\n    sq.Queryf(\"SELECT counter.n + 1 FROM counter WHERE counter.n + 1 <= {}\", 100)\n))\nsq.Postgres.With(counter).Select(counter.Field(\"n\")).From(counter)\n```\n\n### Aggregate functions #aggregate-functions\n\nsq provides some built-in aggregate functions. They return an `sq.Expression` and so can [pretty much be used everywhere](#expr).\n\n```go\nfunc Count(field Field) Expression\nfunc CountStar() Expression\nfunc Sum(num Number) Expression\nfunc Avg(num Number) Expression\nfunc Min(field Field) Expression\nfunc Max(field Field) Expression\n```\n\n### Window functions #window-functions\n\nsq provides some built-in window functions. They return an `sq.Expression` and so can [pretty much be used everywhere](#expr).\n\n```go\nfunc CountOver(field Field, window Window) Expression\nfunc CountStarOver(window Window) Expression\nfunc SumOver(num Number, window Window) Expression\nfunc AvgOver(num Number, window Window) Expression\nfunc MinOver(field Field, window Window) Expression\nfunc MaxOver(field Field, window Window) Expression\nfunc RowNumberOver(window Window) Expression\nfunc RankOver(window Window) Expression\nfunc DenseRankOver(window Window) Expression\nfunc CumeDistOver(window Window) Expression\nfunc FirstValueOver(window Window) Expression\nfunc LastValueOver(window Window) Expression\n```\n\n**Missing window functions**\n\nThe `LeadOver`, `LagOver` and `NtileOver` window functions do not have a representative Go function because they can be overloaded (they have multiple signatures) while Go functions cannot. If you need them, use an `sq.Expr()` as a stand-in.\n\n```sql\nLEAD(a.actor_id) OVER (PARTITION BY a.first_name)\nLEAD(a.actor_id, 2) OVER (PARTITION BY a.first_name)\nLEAD(a.actor_id, 2, 5) OVER (PARTITION BY a.first_name)\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nsq.Expr(\"LEAD({}) OVER (PARTITION BY {})\", a.ACTOR_ID, a.FIRST_NAME)\nsq.Expr(\"LEAD({}, {}) OVER (PARTITION BY {})\", a.ACTOR_ID, 2, a.FIRST_NAME)\nsq.Expr(\"LEAD({}, {}, {}) OVER (PARTITION BY {})\", a.ACTOR_ID, 2, 5, a.FIRST_NAME)\n```\n\n**Using window functions**\n\nTo use a window function, you must create a window using `sq.PartitionBy()`, `sq.OrderBy()` or `sq.BaseWindow()`. You can also pass in `nil` to represent the empty window.\n```sql\n-- Example 1\nSELECT COUNT(*) OVER ()\n-- Example 2\nSELECT SUM(a.actor_id) OVER (PARTITION BY a.first_name)\n-- Example 3\nSELECT AVG(a.actor_id) OVER (\n    PARTITION BY a.first_name, a.last_name\n    ORDER BY a.LAST_UPDATE DESC\n    RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING\n)\n```\n\n```go\na := sq.New[ACTOR](\"a\")\n// Example 1\nsq.Postgres.Select(sq.CountStarOver(nil))\n// Example 2\nsq.Postgres.Select(sq.SumOver(a.ACTOR_ID, sq.PartitionBy(a.FIRST_NAME)))\n// Example 3\nsq.Postgres.Select(sq.AvgOver(a.ACTOR_ID, sq.\n    PartitionBy(a.FIRST_NAME, a.LAST_NAME).\n    OrderBy(a.LAST_UPDATE.Desc()).\n    Frame(\"RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING\"),\n))\n```\n\nSQLite, Postgres and MySQL support the named windows as part of the SELECT query. This allows you to reuse a window definition without having to specify it over and over.\n\n```sql\nSELECT\n    SUM(a.actor_id) OVER w1,\n    MIN(a.actor_id) OVER w2,\n    AVG(a.actor_id) OVER (w1 ORDER BY a.last_update)\nFROM\n    actor AS a\nWINDOW\n    w1 AS (PARTITION BY a.first_name),\n    w2 AS (PARTITION BY a.last_name)\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nw1 := sq.NamedWindow{Name: \"w1\", Definition: sq.PartitionBy(a.FIRST_NAME)}\nw2 := sq.NamedWindow{Name: \"w2\", Definition: sq.PartitionBy(a.LAST_NAME)}\nsq.Postgres.\n    Select(\n        sq.SumOver(a.ACTOR_ID, w1),\n        sq.MinOver(a.ACTOR_ID, w2),\n        sq.AvgOver(a.ACTOR_ID, sq.BaseWindow(w1).OrderBy(a.LAST_UPDATE)),\n    ).\n    From(a).\n    Window(w1, w2)\n```\n\n### UNION, INTERSECT, EXCEPT #union-intersect-except\n\n**Union**\n\n```sql\nSELECT t1.field FROM t1\nUNION\nSELECT t2.field FROM t2\nUNION\nSELECT t3.field FROM t3\n```\n\n```go\nsq.Union(\n    sq.Select(t1.FIELD).From(t1),\n    sq.Select(t2.FIELD).From(t2),\n    sq.Select(t3.FIELD).From(t3),\n)\n```\n\n**Intersect**\n\n```sql\nSELECT t1.field FROM t1\nINTERSECT\nSELECT t2.field FROM t2\nINTERSECT\nSELECT t3.field FROM t3\n```\n\n```go\nsq.Intersect(\n    sq.Select(t1.FIELD).From(t1),\n    sq.Select(t2.FIELD).From(t2),\n    sq.Select(t3.FIELD).From(t3),\n)\n```\n\n**Intersect**\n\n```sql\nSELECT t1.field FROM t1\nEXCEPT\nSELECT t2.field FROM t2\nEXCEPT\nSELECT t3.field FROM t3\n```\n\n```go\nsq.Except(\n    sq.Select(t1.FIELD).From(t1),\n    sq.Select(t2.FIELD).From(t2),\n    sq.Select(t3.FIELD).From(t3),\n)\n```\n\n### ORDER BY #orderby\n\n```sql\nSELECT a.first_name FROM actor AS a ORDER BY a.actor_id DESC\nSELECT a.last_name FROM actor AS a ORDER BY a.actor_id ASC NULLS FIRST\n```\n\n```go\na := sq.New[ACTOR](\"a\")\nsq.Select(a.FIRST_NAME).From(a).OrderBy(a.ACTOR_ID.Desc())\nsq.Select(a.LAST_NAME).From(a).OrderBy(a.ACTOR_ID.Asc().NullsFirst())\n```\n\n## Migrating from go-structured-query #migrating-from-go-structured-query\n\nIf you are migrating to this library from [go-structured-query](https://github.com/bokwoon95/go-structured-query), here are the main changes ([index](#toc-migrating-from-go-structured-query)):\n\n### Tables structs embed sq.TableStruct instead of sq.TableInfo. Struct constructors are no longer needed. #table-struct-changes\n\n**go-structured-query (old)**\n\n```go\n// Code generated by 'sqgen-postgres tables'; DO NOT EDIT.\n\nimport sq \"github.com/bokwoon95/go-structured-query/postgres\"\n\ntype TABLE_ACTOR struct {\n    *sq.TableInfo\n    ACTOR_ID    sq.NumberField\n    FIRST_NAME  sq.StringField\n    LAST_NAME   sq.StringField\n    LAST_UPDATE sq.TimeField\n}\n\nfunc ACTOR() TABLE_ACTOR {\n    tbl := TABLE_USER_ROLES_STUDENTS{TableInfo: &sq.TableInfo{\n        Schema: \"\",\n        Name:   \"actor\",\n    }}\n    tbl.ACTOR_ID = sq.NewNumberField(\"actor_id\", tbl.TableInfo)\n    tbl.FIRST_NAME = sq.NewStringField(\"first_name\", tbl.TableInfo)\n    tbl.LAST_NAME = sq.NewStringField(\"last_name\", tbl.TableInfo)\n    tbl.LAST_UPDATE = sq.NewTimeField(\"last_update\", tbl.TableInfo)\n    return tbl\n}\n\n// Instantiate table.\na := ACTOR()\n\n// Instantiate table with alias.\na := ACTOR().As(\"a\")\n```\n\n**sq (new)**\n\n```go\nimport \"github.com/bokwoon95/sq\"\n\ntype ACTOR struct {\n    sq.TableStruct\n    ACTOR_ID    sq.NumberField\n    FIRST_NAME  sq.StringField\n    LAST_NAME   sq.StringField\n    LAST_UPDATE sq.TimeField\n}\n\n// Instantiate table.\na := sq.New[ACTOR](\"\")\n\n// Instantiate table with alias.\na := sq.New[ACTOR](\"a\")\n```\n\n### The CLI command is now called `sqddl`, not `sqgen-xxx`. Code generation is now optional. #code-generation-changes\n\nThe new `sqddl` command replaces the old `sqgen-postgres` and `sqgen-mysql` commands.\n\n**go-structured-query (old)**\n\n```shell\n# Install the sqgen-postgres command.\n$ go install github.com/bokwoon95/go-structured-query/cmd/sqgen-postgres@latest\n\n# Introspect the database and generate a file called 'tables.go' with package\n# name 'tables' inside the 'tables' directory.\n$ sqgen-postgres tables \\\n    --database 'postgres://user:pass@localhost:5432/sakila?sslmode=disable' \\\n    --pkg tables \\\n    --directory ./tables \\\n    --file tables.go\n```\n\n**sq (new)**\n\n```shell\n# Install the sqddl command.\n$ go install -tags=fts5 github.com/bokwoon95/sqddl@latest\n\n# Introspect the database and generate a file called 'tables.go' with package\n# name 'tables' inside the 'tables' directory.\n#\n# The dialect (sqlite, postgres, mysql or sqlserver) is inferred from the\n# database URL. Refer to https://bokwoon.neocities.org/sqddl#tables for more\n# flags.\n$ sqddl tables \\\n    -db 'postgres://user:pass@localhost:5432/sakila?sslmode=disable' \\\n    -pkg tables\n    -file ./tables/tables.go\n```\n\n**Code generation is now optional**\n\nYou no longer have to generate the table struct code everytime your database schema changes. It is possible to define your table structs as the source of truth and generate migrations from it, using the `sqddl` tool. Read the documentation at [https://bokwoon.neocities.org/sqddl#table-structs](https://bokwoon.neocities.org/sqddl#table-structs) for more information.\n\nAs an example here is an ACTOR table struct, which corresponds to the following CREATE TABLE statement:\n\n```go\n// tables/tables.go\n\nimport \"github.com/bokwoon95/sq\"\n\ntype ACTOR struct {\n    sq.TableStruct\n    ACTOR_ID    sq.NumberField `ddl:\"notnull primarykey identity\"`\n    FIRST_NAME  sq.StringField `ddl:\"type=VARCHAR(45) notnull\"`\n    LAST_NAME   sq.StringField `ddl:\"type=VARCHAR(45) notnull index\"`\n    LAST_UPDATE sq.TimeField   `ddl:\"notnull default=CURRENT_TIMESTAMP\"`\n}\n```\n\n```sql\nCREATE TABLE actor (\n    actor_id INT NOT NULL PRIMARY KEY ALWAYS GENERATED AS IDENTITY,\n    first_name VARCHAR(45) NOT NULL,\n    last_name VARCHAR(45) NOT NULL,\n    last_update TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX actor_last_name_idx ON actor (last_name);\n```\n\nGenerate migrations for the ACTOR struct using `sqddl generate`:\n\n```shell\n$ sqddl generate \\\n    -src 'postgres://user:pass@localhost:5432/sakila' \\\n    -dest tables/tables.go \\\n    -output-dir ./migrations\n```\n\nMore information: [https://bokwoon.neocities.org/sqddl#generate](https://bokwoon.neocities.org/sqddl#generate).\n\n### Fetching #fetch-changes\n\nFetching results has been lifted from a method (SelectRowx, Selectx) into a function (FetchOne, FetchAll). The accumulator function is no longer needed as a slice is generically returned based on the mapper function.\n\n**go-structured-query (old)**\n\n```go\nimport sq \"github.com/bokwoon95/go-structured-query/postgres\"\n\n// Fetch one.\nerr := sq.From(tbl).Where(condition).SelectRowx(mapper).Fetch(db)\n\n// Fetch all.\nerr := sq.From(tbl).Where(condition).Selectx(mapper, accumulator).Fetch(db)\n```\n\n**sq (new)**\n\n```go\nimport \"github.com/bokwoon95/sq\"\n\n// Fetch one.\nresult, err := sq.FetchOne(db, sq.Postgres.From(tbl).Where(condition), mapper)\n\n// Fetch all.\nresults, err := sq.FetchAll(db, sq.Postgres.From(tbl).Where(condition), mapper)\n```\n\n### Exec-ing #exec-changes\n\nSimilar to Fetch, Exec has been lifted from a method into a function. It is no longer necessary to pass in an ExecFlag indicating if you want the lastInsertId or rowsAffected, it will automatically be populated depending on the dialect.\n\n**go-structured-query (old)**\n\n```go\nimport sq \"github.com/bokwoon95/go-structured-query/mysql\"\n\nlastInsertId, rowsAffected, err := sq.InsertInto(tbl).Values(values...).Exec(db, sq.ElastInsertID|sq.ErowsAffected)\n```\n\n**sq (new)**\n\n```go\nimport \"github.com/bokwoon95/sq\"\n\nres, err := sq.Exec(db, sq.MySQL.InsertInto(tbl).Values(values...))\nres.LastInsertId // int64 (valid because MySQL supports LastInsertId)\nres.RowAffected  // int64\n```\n\n### Expr() replaces Fieldf() and Predicatef() #expr-changes\n\n[Fieldf() and Predicatef()](https://bokwoon95.github.io/sq/basics/sql-escape-hatch.html#fieldf-and-predicatef) were used to define Fields and Predicates containing an arbitrary SQL expression. They have been replaced by [Expr()](#expr), which does double duty. The placeholder symbol has been changed from `?` to `{}`.\n\nAssuming we want to replicate the query below:\n\n```sql\nSELECT a.first_name || ' ' || a.last_name\nFROM actor AS a\nWHERE a.last_update + INTERVAL '1 hour' < CURRENT_TIMESTAMP\n```\n\n**go-structured-query (old)**\n\n```go\nimport sq \"github.com/bokwoon95/go-structured-query/postgres\"\n\na := ACTOR().As(\"a\")\nfield := sq.Fieldf(\"? || ' ' || ?\", a.FIRST_NAME, a.LAST_NAME)\npredicate := sq.Predicatef(\"? + INTERVAL '1 hour' < CURRENT_TIMESTAMP\", a.LAST_UPDATE)\nsq.Select(field).From(a).Where(predicate)\n```\n\n**sq (new)**\n\n```go\nimport \"github.com/bokwoon95/sq\"\n\na := sq.New[ACTOR](\"a\")\nfield := sq.Expr(\"{} || ' ' || {}\", a.FIRST_NAME, a.LAST_NAME)\npredicate := sq.Expr(\"{} + INTERVAL '1 hour' < CURRENT_TIMESTAMP\", a.LAST_UPDATE)\nsq.Postgres.Select(field).From(a).Where(predicate)\n```\n\n### Logging #logging-changes\n\nLogging has been lifted out of a method [.WithDefaultLog()](https://bokwoon95.github.io/sq/basics/logging.html) and into a function [sq.Log()](#logging) (which should wrap the database object).\n\n**go-structured-query (old)**\n\n```go\nimport sq \"github.com/bokwoon95/go-structured-query/postgres\"\n\nerr := sq.WithDefaultLog().From(tbl).Where(predicate).Selectx(mapper, accumulator).Fetch(db)\n```\n\n**sq (new)**\n\n```go\nimport \"github.com/bokwoon95/sq\"\n\nresults, err := sq.FetchAll(sq.Log(db), sq.Postgres.From(tbl).Where(predicate))\n```\n\n### Generating type-safe wrappers of PL/pgSQL functions has been removed #generated-plpgsql-function-changes\n\n[go-structured-query supported code-generating wrappers for plpgsql functions](https://bokwoon95.github.io/sq/sql-expressions/plpgsql.html), that feature is not present in sq because I was unsatisfied with the design. As a workaround you should use sq.Expr() to invoke functions instead.\n\n**go-structured-query (old)**\n\n```go\nimport sq \"github.com/bokwoon95/go-structured-query/postgres\"\n\nsq.Select(ADD_NUMS(1, 2)) // ADD_NUMS is code-generated.\n```\n\n**sq (new)**\n\n```go\nimport \"github.com/bokwoon95/sq\"\n\nsq.Postgres.Select(sq.Expr(\"add_nums({}, {})\", 1, 2))\n```\n"
  },
  {
    "path": "sq_test.go",
    "content": "package sq\n\nimport (\n\t\"database/sql\"\n\t\"testing\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n\t\"github.com/google/uuid\"\n)\n\ntype Weekday uint\n\nconst (\n\tWeekdayInvalid Weekday = iota\n\tSunday\n\tMonday\n\tTuesday\n\tWednesday\n\tThursday\n\tFriday\n\tSaturday\n)\n\nfunc (d Weekday) Enumerate() []string {\n\treturn []string{\n\t\tWeekdayInvalid: \"\",\n\t\tSunday:         \"Sunday\",\n\t\tMonday:         \"Monday\",\n\t\tTuesday:        \"Tuesday\",\n\t\tWednesday:      \"Wednesday\",\n\t\tThursday:       \"Thursday\",\n\t\tFriday:         \"Friday\",\n\t\tSaturday:       \"Saturday\",\n\t}\n}\n\nfunc Test_preprocessValue(t *testing.T) {\n\ttype TestTable struct {\n\t\tdescription string\n\t\tdialect     string\n\t\tinput       any\n\t\twantOutput  any\n\t}\n\n\ttests := []TestTable{{\n\t\tdescription: \"empty\",\n\t\tinput:       nil,\n\t\twantOutput:  nil,\n\t}, {\n\t\tdescription: \"driver.Valuer\",\n\t\tinput:       uuid.MustParse(\"a4f952f1-4c45-4e63-bd4e-159ca33c8e20\"),\n\t\twantOutput:  \"a4f952f1-4c45-4e63-bd4e-159ca33c8e20\",\n\t}, {\n\t\tdescription: \"Postgres DialectValuer\",\n\t\tdialect:     DialectPostgres,\n\t\tinput:       UUIDValue(uuid.MustParse(\"a4f952f1-4c45-4e63-bd4e-159ca33c8e20\")),\n\t\twantOutput:  \"a4f952f1-4c45-4e63-bd4e-159ca33c8e20\",\n\t}, {\n\t\tdescription: \"MySQL DialectValuer\",\n\t\tdialect:     DialectMySQL,\n\t\tinput:       UUIDValue(uuid.MustParse(\"a4f952f1-4c45-4e63-bd4e-159ca33c8e20\")),\n\t\twantOutput:  []byte{0xa4, 0xf9, 0x52, 0xf1, 0x4c, 0x45, 0x4e, 0x63, 0xbd, 0x4e, 0x15, 0x9c, 0xa3, 0x3c, 0x8e, 0x20},\n\t}, {\n\t\tdescription: \"Postgres [16]byte\",\n\t\tdialect:     DialectPostgres,\n\t\tinput:       [16]byte{0xa4, 0xf9, 0x52, 0xf1, 0x4c, 0x45, 0x4e, 0x63, 0xbd, 0x4e, 0x15, 0x9c, 0xa3, 0x3c, 0x8e, 0x20},\n\t\twantOutput:  \"a4f952f1-4c45-4e63-bd4e-159ca33c8e20\",\n\t}, {\n\t\tdescription: \"MySQL [16]byte\",\n\t\tdialect:     DialectMySQL,\n\t\tinput:       [16]byte{0xa4, 0xf9, 0x52, 0xf1, 0x4c, 0x45, 0x4e, 0x63, 0xbd, 0x4e, 0x15, 0x9c, 0xa3, 0x3c, 0x8e, 0x20},\n\t\twantOutput:  []byte{0xa4, 0xf9, 0x52, 0xf1, 0x4c, 0x45, 0x4e, 0x63, 0xbd, 0x4e, 0x15, 0x9c, 0xa3, 0x3c, 0x8e, 0x20},\n\t}, {\n\t\tdescription: \"Enumeration\",\n\t\tinput:       Monday,\n\t\twantOutput:  \"Monday\",\n\t}, {\n\t\tdescription: \"int\",\n\t\tinput:       42,\n\t\twantOutput:  42,\n\t}, {\n\t\tdescription: \"sql.NullString\",\n\t\tinput: sql.NullString{\n\t\t\tValid:  false,\n\t\t\tString: \"lorem ipsum dolor sit amet\",\n\t\t},\n\t\twantOutput: nil,\n\t}}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tgotOutput, err := preprocessValue(tt.dialect, tt.input)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(testutil.Callers(), err)\n\t\t\t}\n\t\t\tif diff := testutil.Diff(gotOutput, tt.wantOutput); diff != \"\" {\n\t\t\t\tt.Error(testutil.Callers(), diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "update_query.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n)\n\n// UpdateQuery represents an SQL UPDATE query.\ntype UpdateQuery struct {\n\tDialect      string\n\tColumnMapper func(*Column)\n\t// WITH\n\tCTEs []CTE\n\t// UPDATE\n\tUpdateTable Table\n\t// FROM\n\tFromTable  Table\n\tJoinTables []JoinTable\n\t// SET\n\tAssignments []Assignment\n\t// WHERE\n\tWherePredicate Predicate\n\t// ORDER BY\n\tOrderByFields []Field\n\t// LIMIT\n\tLimitRows any\n\t// RETURNING\n\tReturningFields []Field\n}\n\nvar _ Query = (*UpdateQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q UpdateQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) (err error) {\n\tif q.ColumnMapper != nil {\n\t\tcol := &Column{\n\t\t\tdialect:  q.Dialect,\n\t\t\tisUpdate: true,\n\t\t}\n\t\tdefer mapperFunctionPanicked(&err)\n\t\tq.ColumnMapper(col)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tq.Assignments = col.assignments\n\t}\n\t// Table Policies\n\tvar policies []Predicate\n\tpolicies, err = appendPolicy(ctx, dialect, policies, q.UpdateTable)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"UPDATE %s Policy: %w\", toString(q.Dialect, q.UpdateTable), err)\n\t}\n\tpolicies, err = appendPolicy(ctx, dialect, policies, q.FromTable)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"FROM %s Policy: %w\", toString(q.Dialect, q.FromTable), err)\n\t}\n\tfor _, joinTable := range q.JoinTables {\n\t\tpolicies, err = appendPolicy(ctx, dialect, policies, joinTable.Table)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s %s Policy: %w\", joinTable.JoinOperator, joinTable.Table, err)\n\t\t}\n\t}\n\tif len(policies) > 0 {\n\t\tif q.WherePredicate != nil {\n\t\t\tpolicies = append(policies, q.WherePredicate)\n\t\t}\n\t\tq.WherePredicate = And(policies...)\n\t}\n\t// WITH\n\tif len(q.CTEs) > 0 {\n\t\terr = writeCTEs(ctx, dialect, buf, args, params, q.CTEs)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"WITH: %w\", err)\n\t\t}\n\t}\n\t// UPDATE\n\tbuf.WriteString(\"UPDATE \")\n\tif q.UpdateTable == nil {\n\t\treturn fmt.Errorf(\"no table provided to UPDATE\")\n\t}\n\terr = q.UpdateTable.WriteSQL(ctx, dialect, buf, args, params)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"UPDATE: %w\", err)\n\t}\n\tif dialect != DialectSQLServer {\n\t\tif alias := getAlias(q.UpdateTable); alias != \"\" {\n\t\t\tbuf.WriteString(\" AS \" + QuoteIdentifier(dialect, alias))\n\t\t}\n\t}\n\tif len(q.Assignments) == 0 {\n\t\treturn fmt.Errorf(\"no fields to update\")\n\t}\n\t// SET (not mysql)\n\tif dialect != DialectMySQL {\n\t\tbuf.WriteString(\" SET \")\n\t\terr = Assignments(q.Assignments).WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"SET: %w\", err)\n\t\t}\n\t}\n\t// OUTPUT\n\tif len(q.ReturningFields) > 0 && dialect == DialectSQLServer {\n\t\tbuf.WriteString(\" OUTPUT \")\n\t\terr = writeFieldsWithPrefix(ctx, dialect, buf, args, params, q.ReturningFields, \"INSERTED\", true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t// FROM\n\tif q.FromTable != nil {\n\t\tif dialect == DialectMySQL {\n\t\t\treturn fmt.Errorf(\"mysql UPDATE does not support FROM\")\n\t\t}\n\t\tbuf.WriteString(\" FROM \")\n\t\terr = q.FromTable.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"FROM: %w\", err)\n\t\t}\n\t\tif alias := getAlias(q.FromTable); alias != \"\" {\n\t\t\tbuf.WriteString(\" AS \" + QuoteIdentifier(dialect, alias) + quoteTableColumns(dialect, q.FromTable))\n\t\t}\n\t}\n\t// JOIN\n\tif len(q.JoinTables) > 0 {\n\t\tif q.FromTable == nil && dialect != DialectMySQL {\n\t\t\treturn fmt.Errorf(\"%s can't JOIN without a FROM table\", dialect)\n\t\t}\n\t\tbuf.WriteString(\" \")\n\t\terr = writeJoinTables(ctx, dialect, buf, args, params, q.JoinTables)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"JOIN: %w\", err)\n\t\t}\n\t}\n\t// SET (mysql)\n\tif dialect == DialectMySQL {\n\t\tbuf.WriteString(\" SET \")\n\t\terr = Assignments(q.Assignments).WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"SET: %w\", err)\n\t\t}\n\t}\n\t// WHERE\n\tif q.WherePredicate != nil {\n\t\tbuf.WriteString(\" WHERE \")\n\t\tswitch predicate := q.WherePredicate.(type) {\n\t\tcase VariadicPredicate:\n\t\t\tpredicate.Toplevel = true\n\t\t\terr = predicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"WHERE: %w\", err)\n\t\t\t}\n\t\tdefault:\n\t\t\terr = q.WherePredicate.WriteSQL(ctx, dialect, buf, args, params)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"WHERE: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\t// ORDER BY\n\tif len(q.OrderByFields) > 0 {\n\t\tif dialect != DialectMySQL {\n\t\t\treturn fmt.Errorf(\"%s UPDATE does not support ORDER BY\", dialect)\n\t\t}\n\t\tbuf.WriteString(\" ORDER BY \")\n\t\terr = writeFields(ctx, dialect, buf, args, params, q.OrderByFields, false)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"ORDER BY: %w\", err)\n\t\t}\n\t}\n\t// LIMIT\n\tif q.LimitRows != nil {\n\t\tif dialect != DialectMySQL {\n\t\t\treturn fmt.Errorf(\"%s UPDATE does not support LIMIT\", dialect)\n\t\t}\n\t\tbuf.WriteString(\" LIMIT \")\n\t\terr = WriteValue(ctx, dialect, buf, args, params, q.LimitRows)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"LIMIT: %w\", err)\n\t\t}\n\t}\n\t// RETURNING\n\tif len(q.ReturningFields) > 0 && dialect != DialectSQLServer {\n\t\tif dialect != DialectPostgres && dialect != DialectSQLite {\n\t\t\treturn fmt.Errorf(\"%s UPDATE does not support RETURNING\", dialect)\n\t\t}\n\t\tbuf.WriteString(\" RETURNING \")\n\t\terr = writeFields(ctx, dialect, buf, args, params, q.ReturningFields, true)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"RETURNING: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// Update returns a new UpdateQuery.\nfunc Update(table Table) UpdateQuery {\n\treturn UpdateQuery{UpdateTable: table}\n}\n\n// Set sets the Assignments field of the UpdateQuery.\nfunc (q UpdateQuery) Set(assignments ...Assignment) UpdateQuery {\n\tq.Assignments = append(q.Assignments, assignments...)\n\treturn q\n}\n\n// SetFunc sets the ColumnMapper field of the UpdateQuery.\nfunc (q UpdateQuery) SetFunc(colmapper func(*Column)) UpdateQuery {\n\tq.ColumnMapper = colmapper\n\treturn q\n}\n\n// Where appends to the WherePredicate field of the UpdateQuery.\nfunc (q UpdateQuery) Where(predicates ...Predicate) UpdateQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q UpdateQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\tswitch q.Dialect {\n\tcase DialectPostgres, DialectSQLite:\n\t\tif len(q.ReturningFields) == 0 {\n\t\t\tq.ReturningFields = fields\n\t\t\treturn q, true\n\t\t}\n\t\treturn q, false\n\tdefault:\n\t\treturn q, false\n\t}\n}\n\n// GetFetchableFields returns the fetchable fields of the query.\nfunc (q UpdateQuery) GetFetchableFields() []Field {\n\tswitch q.Dialect {\n\tcase DialectPostgres, DialectSQLite:\n\t\treturn q.ReturningFields\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// GetDialect implements the Query interface.\nfunc (q UpdateQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the query.\nfunc (q UpdateQuery) SetDialect(dialect string) UpdateQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// SQLiteUpdateQuery represents an SQLite UPDATE query.\ntype SQLiteUpdateQuery UpdateQuery\n\nvar _ Query = (*SQLiteUpdateQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q SQLiteUpdateQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn UpdateQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// Update returns a new SQLiteUpdateQuery.\nfunc (b sqliteQueryBuilder) Update(table Table) SQLiteUpdateQuery {\n\treturn SQLiteUpdateQuery{\n\t\tDialect:     DialectSQLite,\n\t\tCTEs:        b.ctes,\n\t\tUpdateTable: table,\n\t}\n}\n\n// Set sets the Assignments field of the SQLiteUpdateQuery.\nfunc (q SQLiteUpdateQuery) Set(assignments ...Assignment) SQLiteUpdateQuery {\n\tq.Assignments = append(q.Assignments, assignments...)\n\treturn q\n}\n\n// SetFunc sets the ColumnMapper of the SQLiteUpdateQuery.\nfunc (q SQLiteUpdateQuery) SetFunc(colmapper func(*Column)) SQLiteUpdateQuery {\n\tq.ColumnMapper = colmapper\n\treturn q\n}\n\n// From sets the FromTable field of the SQLiteUpdateQuery.\nfunc (q SQLiteUpdateQuery) From(table Table) SQLiteUpdateQuery {\n\tq.FromTable = table\n\treturn q\n}\n\n// Join joins a new Table to the SQLiteUpdateQuery.\nfunc (q SQLiteUpdateQuery) Join(table Table, predicates ...Predicate) SQLiteUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the SQLiteUpdateQuery.\nfunc (q SQLiteUpdateQuery) LeftJoin(table Table, predicates ...Predicate) SQLiteUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the SQLiteUpdateQuery.\nfunc (q SQLiteUpdateQuery) CrossJoin(table Table) SQLiteUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the SQLiteUpdateQuery with a custom join\n// operator.\nfunc (q SQLiteUpdateQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) SQLiteUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// JoinUsing joins a new Table to the SQLiteUpdateQuery with the USING operator.\nfunc (q SQLiteUpdateQuery) JoinUsing(table Table, fields ...Field) SQLiteUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, JoinUsing(table, fields...))\n\treturn q\n}\n\n// Where appends to the WherePredicate field of the SQLiteUpdateQuery.\nfunc (q SQLiteUpdateQuery) Where(predicates ...Predicate) SQLiteUpdateQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// Returning sets the ReturningFields field of the SQLiteUpdateQuery.\nfunc (q SQLiteUpdateQuery) Returning(fields ...Field) SQLiteUpdateQuery {\n\tq.ReturningFields = append(q.ReturningFields, fields...)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q SQLiteUpdateQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn UpdateQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the SQLiteUpdateQuery.\nfunc (q SQLiteUpdateQuery) GetFetchableFields() []Field {\n\treturn UpdateQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q SQLiteUpdateQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the SQLiteUpdateQuery.\nfunc (q SQLiteUpdateQuery) SetDialect(dialect string) SQLiteUpdateQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// PostgresUpdateQuery represents a Postgres UPDATE query.\ntype PostgresUpdateQuery UpdateQuery\n\nvar _ Query = (*PostgresUpdateQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q PostgresUpdateQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn UpdateQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// Update returns a new PostgresUpdateQuery.\nfunc (b postgresQueryBuilder) Update(table Table) PostgresUpdateQuery {\n\treturn PostgresUpdateQuery{\n\t\tDialect:     DialectPostgres,\n\t\tCTEs:        b.ctes,\n\t\tUpdateTable: table,\n\t}\n}\n\n// Set sets the Assignments field of the PostgresUpdateQuery.\nfunc (q PostgresUpdateQuery) Set(assignments ...Assignment) PostgresUpdateQuery {\n\tq.Assignments = append(q.Assignments, assignments...)\n\treturn q\n}\n\n// SetFunc sets the ColumnMapper of the PostgresUpdateQuery.\nfunc (q PostgresUpdateQuery) SetFunc(colmapper func(*Column)) PostgresUpdateQuery {\n\tq.ColumnMapper = colmapper\n\treturn q\n}\n\n// From sets the FromTable field of the PostgresUpdateQuery.\nfunc (q PostgresUpdateQuery) From(table Table) PostgresUpdateQuery {\n\tq.FromTable = table\n\treturn q\n}\n\n// Join joins a new Table to the PostgresUpdateQuery.\nfunc (q PostgresUpdateQuery) Join(table Table, predicates ...Predicate) PostgresUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the PostgresUpdateQuery.\nfunc (q PostgresUpdateQuery) LeftJoin(table Table, predicates ...Predicate) PostgresUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// FullJoin full joins a new Table to the PostgresUpdateQuery.\nfunc (q PostgresUpdateQuery) FullJoin(table Table, predicates ...Predicate) PostgresUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, FullJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the PostgresUpdateQuery.\nfunc (q PostgresUpdateQuery) CrossJoin(table Table) PostgresUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the PostgresUpdateQuery with a custom join\n// operator.\nfunc (q PostgresUpdateQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) PostgresUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// JoinUsing joins a new Table to the PostgresUpdateQuery with the USING operator.\nfunc (q PostgresUpdateQuery) JoinUsing(table Table, fields ...Field) PostgresUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, JoinUsing(table, fields...))\n\treturn q\n}\n\n// Where appends to the WherePredicate field of the PostgresUpdateQuery.\nfunc (q PostgresUpdateQuery) Where(predicates ...Predicate) PostgresUpdateQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// Returning sets the ReturningFields field of the PostgresUpdateQuery.\nfunc (q PostgresUpdateQuery) Returning(fields ...Field) PostgresUpdateQuery {\n\tq.ReturningFields = append(q.ReturningFields, fields...)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q PostgresUpdateQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn UpdateQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the PostgresUpdateQuery.\nfunc (q PostgresUpdateQuery) GetFetchableFields() []Field {\n\treturn UpdateQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q PostgresUpdateQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the PostgresUpdateQuery.\nfunc (q PostgresUpdateQuery) SetDialect(dialect string) PostgresUpdateQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// MySQLUpdateQuery represents a MySQL UPDATE query.\ntype MySQLUpdateQuery UpdateQuery\n\nvar _ Query = (*MySQLUpdateQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q MySQLUpdateQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn UpdateQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// Update returns a new MySQLUpdateQuery.\nfunc (b mysqlQueryBuilder) Update(table Table) MySQLUpdateQuery {\n\tq := MySQLUpdateQuery{\n\t\tDialect:     DialectMySQL,\n\t\tCTEs:        b.ctes,\n\t\tUpdateTable: table,\n\t}\n\treturn q\n}\n\n// Join joins a new Table to the MySQLUpdateQuery.\nfunc (q MySQLUpdateQuery) Join(table Table, predicates ...Predicate) MySQLUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the MySQLUpdateQuery.\nfunc (q MySQLUpdateQuery) LeftJoin(table Table, predicates ...Predicate) MySQLUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// FullJoin full joins a new Table to the MySQLUpdateQuery.\nfunc (q MySQLUpdateQuery) FullJoin(table Table, predicates ...Predicate) MySQLUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, FullJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the MySQLUpdateQuery.\nfunc (q MySQLUpdateQuery) CrossJoin(table Table) MySQLUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the MySQLUpdateQuery with a custom join\n// operator.\nfunc (q MySQLUpdateQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) MySQLUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// JoinUsing joins a new Table to the MySQLUpdateQuery with the USING operator.\nfunc (q MySQLUpdateQuery) JoinUsing(table Table, fields ...Field) MySQLUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, JoinUsing(table, fields...))\n\treturn q\n}\n\n// Set sets the Assignments field of the MySQLUpdateQuery.\nfunc (q MySQLUpdateQuery) Set(assignments ...Assignment) MySQLUpdateQuery {\n\tq.Assignments = append(q.Assignments, assignments...)\n\treturn q\n}\n\n// SetFunc sets the ColumnMapper of the MySQLUpdateQuery.\nfunc (q MySQLUpdateQuery) SetFunc(colmapper func(*Column)) MySQLUpdateQuery {\n\tq.ColumnMapper = colmapper\n\treturn q\n}\n\n// Where appends to the WherePredicate field of the MySQLUpdateQuery.\nfunc (q MySQLUpdateQuery) Where(predicates ...Predicate) MySQLUpdateQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// OrderBy sets the OrderByFields of the MySQLUpdateQuery.\nfunc (q MySQLUpdateQuery) OrderBy(fields ...Field) MySQLUpdateQuery {\n\tq.OrderByFields = append(q.OrderByFields, fields...)\n\treturn q\n}\n\n// Limit sets the LimitRows field of the MySQLUpdateQuery.\nfunc (q MySQLUpdateQuery) Limit(limit any) MySQLUpdateQuery {\n\tq.LimitRows = limit\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q MySQLUpdateQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn UpdateQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the MySQLUpdateQuery.\nfunc (q MySQLUpdateQuery) GetFetchableFields() []Field {\n\treturn UpdateQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q MySQLUpdateQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the MySQLUpdateQuery.\nfunc (q MySQLUpdateQuery) SetDialect(dialect string) MySQLUpdateQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n\n// SQLServerUpdateQuery represents an SQL Server UPDATE query.\ntype SQLServerUpdateQuery UpdateQuery\n\nvar _ Query = (*SQLServerUpdateQuery)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (q SQLServerUpdateQuery) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\treturn UpdateQuery(q).WriteSQL(ctx, dialect, buf, args, params)\n}\n\n// Update returns a new SQLServerUpdateQuery.\nfunc (b sqlserverQueryBuilder) Update(table Table) SQLServerUpdateQuery {\n\treturn SQLServerUpdateQuery{\n\t\tDialect:     DialectSQLServer,\n\t\tCTEs:        b.ctes,\n\t\tUpdateTable: table,\n\t}\n}\n\n// Set sets the Assignments field of the SQLServerUpdateQuery.\nfunc (q SQLServerUpdateQuery) Set(assignments ...Assignment) SQLServerUpdateQuery {\n\tq.Assignments = append(q.Assignments, assignments...)\n\treturn q\n}\n\n// SetFunc sets the ColumnMapper of the SQLServerUpdateQuery.\nfunc (q SQLServerUpdateQuery) SetFunc(colmapper func(*Column)) SQLServerUpdateQuery {\n\tq.ColumnMapper = colmapper\n\treturn q\n}\n\n// From sets the FromTable field of the SQLServerUpdateQuery.\nfunc (q SQLServerUpdateQuery) From(table Table) SQLServerUpdateQuery {\n\tq.FromTable = table\n\treturn q\n}\n\n// Join joins a new Table to the SQLServerUpdateQuery.\nfunc (q SQLServerUpdateQuery) Join(table Table, predicates ...Predicate) SQLServerUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, Join(table, predicates...))\n\treturn q\n}\n\n// LeftJoin left joins a new Table to the SQLServerUpdateQuery.\nfunc (q SQLServerUpdateQuery) LeftJoin(table Table, predicates ...Predicate) SQLServerUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, LeftJoin(table, predicates...))\n\treturn q\n}\n\n// FullJoin full joins a new Table to the SQLServerUpdateQuery.\nfunc (q SQLServerUpdateQuery) FullJoin(table Table, predicates ...Predicate) SQLServerUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, FullJoin(table, predicates...))\n\treturn q\n}\n\n// CrossJoin cross joins a new Table to the SQLServerUpdateQuery.\nfunc (q SQLServerUpdateQuery) CrossJoin(table Table) SQLServerUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, CrossJoin(table))\n\treturn q\n}\n\n// CustomJoin joins a new Table to the SQLServerUpdateQuery with a custom join\n// operator.\nfunc (q SQLServerUpdateQuery) CustomJoin(joinOperator string, table Table, predicates ...Predicate) SQLServerUpdateQuery {\n\tq.JoinTables = append(q.JoinTables, CustomJoin(joinOperator, table, predicates...))\n\treturn q\n}\n\n// Where appends to the WherePredicate field of the SQLServerUpdateQuery.\nfunc (q SQLServerUpdateQuery) Where(predicates ...Predicate) SQLServerUpdateQuery {\n\tq.WherePredicate = appendPredicates(q.WherePredicate, predicates)\n\treturn q\n}\n\n// SetFetchableFields implements the Query interface.\nfunc (q SQLServerUpdateQuery) SetFetchableFields(fields []Field) (query Query, ok bool) {\n\treturn UpdateQuery(q).SetFetchableFields(fields)\n}\n\n// GetFetchableFields returns the fetchable fields of the SQLServerUpdateQuery.\nfunc (q SQLServerUpdateQuery) GetFetchableFields() []Field {\n\treturn UpdateQuery(q).GetFetchableFields()\n}\n\n// GetDialect implements the Query interface.\nfunc (q SQLServerUpdateQuery) GetDialect() string { return q.Dialect }\n\n// SetDialect sets the dialect of the SQLServerUpdateQuery.\nfunc (q SQLServerUpdateQuery) SetDialect(dialect string) SQLServerUpdateQuery {\n\tq.Dialect = dialect\n\treturn q\n}\n"
  },
  {
    "path": "update_query_test.go",
    "content": "package sq\n\nimport (\n\t\"testing\"\n\n\t\"github.com/bokwoon95/sq/internal/testutil\"\n)\n\nfunc TestSQLiteUpdateQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := SQLite.Update(a).Returning(a.FIRST_NAME).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectSQLite)\n\t\tfields := q1.GetFetchableFields()\n\t\tif diff := testutil.Diff(fields, []Field{a.FIRST_NAME}); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Set\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLite.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tUpdate(a).\n\t\t\tSet(\n\t\t\t\ta.FIRST_NAME.SetString(\"bob\"),\n\t\t\t\ta.LAST_NAME.SetString(\"the builder\"),\n\t\t\t).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1), a.LAST_UPDATE.IsNotNull()).\n\t\t\tReturning(a.ACTOR_ID)\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" UPDATE actor AS a\" +\n\t\t\t\" SET first_name = $1, last_name = $2\" +\n\t\t\t\" WHERE a.actor_id = $3 AND a.last_update IS NOT NULL\" +\n\t\t\t\" RETURNING a.actor_id\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"SetFunc\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLite.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tUpdate(a).\n\t\t\tSetFunc(func(col *Column) {\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"bob\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"the builder\")\n\t\t\t}).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1))\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" UPDATE actor AS a\" +\n\t\t\t\" SET first_name = $1, last_name = $2\" +\n\t\t\t\" WHERE a.actor_id = $3\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"UPDATE with JOIN\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLite.\n\t\t\tUpdate(a).\n\t\t\tSet(\n\t\t\t\ta.FIRST_NAME.SetString(\"bob\"),\n\t\t\t\ta.LAST_NAME.SetString(\"the builder\"),\n\t\t\t).\n\t\t\tFrom(a).\n\t\t\tJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)).\n\t\t\tLeftJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)).\n\t\t\tCrossJoin(a).\n\t\t\tCustomJoin(\",\", a).\n\t\t\tJoinUsing(a, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1))\n\t\ttt.wantQuery = \"UPDATE actor AS a\" +\n\t\t\t\" SET first_name = $1, last_name = $2\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" JOIN actor AS a ON a.actor_id = a.actor_id\" +\n\t\t\t\" LEFT JOIN actor AS a ON a.actor_id = a.actor_id\" +\n\t\t\t\" CROSS JOIN actor AS a\" +\n\t\t\t\" , actor AS a\" +\n\t\t\t\" JOIN actor AS a USING (first_name, last_name)\" +\n\t\t\t\" WHERE a.actor_id = $3\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestPostgresUpdateQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := Postgres.Update(a).Returning(a.FIRST_NAME).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectPostgres)\n\t\tfields := q1.GetFetchableFields()\n\t\tif diff := testutil.Diff(fields, []Field{a.FIRST_NAME}); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif !ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Set\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tUpdate(a).\n\t\t\tSet(\n\t\t\t\ta.FIRST_NAME.SetString(\"bob\"),\n\t\t\t\ta.LAST_NAME.SetString(\"the builder\"),\n\t\t\t).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1), a.LAST_UPDATE.IsNotNull()).\n\t\t\tReturning(a.ACTOR_ID)\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" UPDATE actor AS a\" +\n\t\t\t\" SET first_name = $1, last_name = $2\" +\n\t\t\t\" WHERE a.actor_id = $3 AND a.last_update IS NOT NULL\" +\n\t\t\t\" RETURNING a.actor_id\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"SetFunc\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tUpdate(a).\n\t\t\tSetFunc(func(col *Column) {\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"bob\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"the builder\")\n\t\t\t}).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1))\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" UPDATE actor AS a\" +\n\t\t\t\" SET first_name = $1, last_name = $2\" +\n\t\t\t\" WHERE a.actor_id = $3\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"UPDATE with JOIN\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = Postgres.\n\t\t\tUpdate(a).\n\t\t\tSet(\n\t\t\t\ta.FIRST_NAME.SetString(\"bob\"),\n\t\t\t\ta.LAST_NAME.SetString(\"the builder\"),\n\t\t\t).\n\t\t\tFrom(a).\n\t\t\tJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)).\n\t\t\tLeftJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)).\n\t\t\tFullJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)).\n\t\t\tCrossJoin(a).\n\t\t\tCustomJoin(\",\", a).\n\t\t\tJoinUsing(a, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1))\n\t\ttt.wantQuery = \"UPDATE actor AS a\" +\n\t\t\t\" SET first_name = $1, last_name = $2\" +\n\t\t\t\" FROM actor AS a\" +\n\t\t\t\" JOIN actor AS a ON a.actor_id = a.actor_id\" +\n\t\t\t\" LEFT JOIN actor AS a ON a.actor_id = a.actor_id\" +\n\t\t\t\" FULL JOIN actor AS a ON a.actor_id = a.actor_id\" +\n\t\t\t\" CROSS JOIN actor AS a\" +\n\t\t\t\" , actor AS a\" +\n\t\t\t\" JOIN actor AS a USING (first_name, last_name)\" +\n\t\t\t\" WHERE a.actor_id = $3\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestMySQLUpdateQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"a\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := MySQL.Update(a).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectMySQL)\n\t\tfields := q1.GetFetchableFields()\n\t\tif len(fields) != 0 {\n\t\t\tt.Error(testutil.Callers(), \"expected 0 fields but got %v\", fields)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Set\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tUpdate(a).\n\t\t\tSet(\n\t\t\t\ta.FIRST_NAME.SetString(\"bob\"),\n\t\t\t\ta.LAST_NAME.SetString(\"the builder\"),\n\t\t\t).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1))\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" UPDATE actor AS a\" +\n\t\t\t\" SET a.first_name = ?, a.last_name = ?\" +\n\t\t\t\" WHERE a.actor_id = ?\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"SetFunc\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tUpdate(a).\n\t\t\tSetFunc(func(col *Column) {\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"bob\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"the builder\")\n\t\t\t}).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1))\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" UPDATE actor AS a\" +\n\t\t\t\" SET a.first_name = ?, a.last_name = ?\" +\n\t\t\t\" WHERE a.actor_id = ?\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"UPDATE with JOIN, ORDER BY, LIMIT\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = MySQL.\n\t\t\tUpdate(a).\n\t\t\tJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)).\n\t\t\tLeftJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)).\n\t\t\tFullJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)).\n\t\t\tCrossJoin(a).\n\t\t\tCustomJoin(\",\", a).\n\t\t\tJoinUsing(a, a.FIRST_NAME, a.LAST_NAME).\n\t\t\tSet(\n\t\t\t\ta.FIRST_NAME.SetString(\"bob\"),\n\t\t\t\ta.LAST_NAME.SetString(\"the builder\"),\n\t\t\t).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1)).\n\t\t\tOrderBy(a.ACTOR_ID).\n\t\t\tLimit(5)\n\t\ttt.wantQuery = \"UPDATE actor AS a\" +\n\t\t\t\" JOIN actor AS a ON a.actor_id = a.actor_id\" +\n\t\t\t\" LEFT JOIN actor AS a ON a.actor_id = a.actor_id\" +\n\t\t\t\" FULL JOIN actor AS a ON a.actor_id = a.actor_id\" +\n\t\t\t\" CROSS JOIN actor AS a\" +\n\t\t\t\" , actor AS a\" +\n\t\t\t\" JOIN actor AS a USING (first_name, last_name)\" +\n\t\t\t\" SET a.first_name = ?, a.last_name = ?\" +\n\t\t\t\" WHERE a.actor_id = ?\" +\n\t\t\t\" ORDER BY a.actor_id\" +\n\t\t\t\" LIMIT ?\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1, 5}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestSQLServerUpdateQuery(t *testing.T) {\n\ttype ACTOR struct {\n\t\tTableStruct\n\t\tACTOR_ID    NumberField\n\t\tFIRST_NAME  StringField\n\t\tLAST_NAME   StringField\n\t\tLAST_UPDATE TimeField\n\t}\n\ta := New[ACTOR](\"\")\n\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := SQLServer.Update(a).SetDialect(\"lorem ipsum\")\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t\tq1 = q1.SetDialect(DialectSQLServer)\n\t\tfields := q1.GetFetchableFields()\n\t\tif len(fields) != 0 {\n\t\t\tt.Error(testutil.Callers(), \"expected 0 fields but got %v\", fields)\n\t\t}\n\t\t_, ok := q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t\tq1.ReturningFields = q1.ReturningFields[:0]\n\t\t_, ok = q1.SetFetchableFields([]Field{a.LAST_NAME})\n\t\tif ok {\n\t\t\tt.Fatal(testutil.Callers(), \"field should not have been set\")\n\t\t}\n\t})\n\n\tt.Run(\"Set\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLServer.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tUpdate(a).\n\t\t\tSet(\n\t\t\t\ta.FIRST_NAME.SetString(\"bob\"),\n\t\t\t\ta.LAST_NAME.SetString(\"the builder\"),\n\t\t\t).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1))\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" UPDATE actor\" +\n\t\t\t\" SET first_name = @p1, last_name = @p2\" +\n\t\t\t\" WHERE actor.actor_id = @p3\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"SetFunc\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLServer.\n\t\t\tWith(NewCTE(\"cte\", nil, Queryf(\"SELECT 1\"))).\n\t\t\tUpdate(a).\n\t\t\tSetFunc(func(col *Column) {\n\t\t\t\tcol.SetString(a.FIRST_NAME, \"bob\")\n\t\t\t\tcol.SetString(a.LAST_NAME, \"the builder\")\n\t\t\t}).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1))\n\t\ttt.wantQuery = \"WITH cte AS (SELECT 1)\" +\n\t\t\t\" UPDATE actor\" +\n\t\t\t\" SET first_name = @p1, last_name = @p2\" +\n\t\t\t\" WHERE actor.actor_id = @p3\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1}\n\t\ttt.assert(t)\n\t})\n\n\tt.Run(\"UPDATE with JOIN\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = SQLServer.\n\t\t\tUpdate(a).\n\t\t\tSet(\n\t\t\t\ta.FIRST_NAME.SetString(\"bob\"),\n\t\t\t\ta.LAST_NAME.SetString(\"the builder\"),\n\t\t\t).\n\t\t\tFrom(a).\n\t\t\tJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)).\n\t\t\tLeftJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)).\n\t\t\tFullJoin(a, a.ACTOR_ID.Eq(a.ACTOR_ID)).\n\t\t\tCrossJoin(a).\n\t\t\tCustomJoin(\",\", a).\n\t\t\tWhere(a.ACTOR_ID.EqInt(1))\n\t\ttt.wantQuery = \"UPDATE actor\" +\n\t\t\t\" SET first_name = @p1, last_name = @p2\" +\n\t\t\t\" FROM actor\" +\n\t\t\t\" JOIN actor ON actor.actor_id = actor.actor_id\" +\n\t\t\t\" LEFT JOIN actor ON actor.actor_id = actor.actor_id\" +\n\t\t\t\" FULL JOIN actor ON actor.actor_id = actor.actor_id\" +\n\t\t\t\" CROSS JOIN actor\" +\n\t\t\t\" , actor\" +\n\t\t\t\" WHERE actor.actor_id = @p3\"\n\t\ttt.wantArgs = []any{\"bob\", \"the builder\", 1}\n\t\ttt.assert(t)\n\t})\n}\n\nfunc TestUpdateQuery(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tq1 := UpdateQuery{UpdateTable: Expr(\"tbl\"), Dialect: \"lorem ipsum\"}\n\t\tif diff := testutil.Diff(q1.GetDialect(), \"lorem ipsum\"); diff != \"\" {\n\t\t\tt.Error(testutil.Callers(), diff)\n\t\t}\n\t})\n\n\tf1, f2, f3 := Expr(\"f1\"), Expr(\"f2\"), Expr(\"f3\")\n\tcolmapper := func(col *Column) {\n\t\tcol.Set(f1, 1)\n\t\tcol.Set(f2, 2)\n\t\tcol.Set(f3, 3)\n\t}\n\n\tt.Run(\"PolicyTable\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar tt TestTable\n\t\ttt.item = UpdateQuery{\n\t\t\tUpdateTable:    policyTableStub{policy: And(Expr(\"1 = 1\"), Expr(\"2 = 2\"))},\n\t\t\tColumnMapper:   colmapper,\n\t\t\tWherePredicate: Expr(\"3 = 3\"),\n\t\t}\n\t\ttt.wantQuery = \"UPDATE policy_table_stub SET f1 = ?, f2 = ?, f3 = ? WHERE (1 = 1 AND 2 = 2) AND 3 = 3\"\n\t\ttt.wantArgs = []any{1, 2, 3}\n\t\ttt.assert(t)\n\t})\n\n\tnotOKTests := []TestTable{{\n\t\tdescription: \"nil UpdateTable not allowed\",\n\t\titem: UpdateQuery{\n\t\t\tUpdateTable:  nil,\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"empty Assignments not allowed\",\n\t\titem: UpdateQuery{\n\t\t\tUpdateTable: Expr(\"tbl\"),\n\t\t\tAssignments: nil,\n\t\t},\n\t}, {\n\t\tdescription: \"mysql does not support FROM\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:      DialectMySQL,\n\t\t\tUpdateTable:  Expr(\"tbl\"),\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"dialect does not allow JOIN without FROM\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:     DialectPostgres,\n\t\t\tUpdateTable: Expr(\"tbl\"),\n\t\t\tFromTable:   nil,\n\t\t\tJoinTables: []JoinTable{\n\t\t\t\tJoin(Expr(\"tbl\"), Expr(\"1 = 1\")),\n\t\t\t},\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"dialect does not support ORDER BY\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:       DialectPostgres,\n\t\t\tUpdateTable:   Expr(\"tbl\"),\n\t\t\tColumnMapper:  colmapper,\n\t\t\tOrderByFields: Fields{f1},\n\t\t},\n\t}, {\n\t\tdescription: \"dialect does not support LIMIT\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tUpdateTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tLimitRows:    5,\n\t\t},\n\t}, {\n\t\tdescription: \"dialect does not support RETURNING\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:         DialectMySQL,\n\t\t\tUpdateTable:     Expr(\"tbl\"),\n\t\t\tColumnMapper:    colmapper,\n\t\t\tReturningFields: Fields{f1, f2, f3},\n\t\t},\n\t}}\n\n\tfor _, tt := range notOKTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertNotOK(t)\n\t\t})\n\t}\n\n\terrTests := []TestTable{{\n\t\tdescription: \"ColumnMapper err\",\n\t\titem: UpdateQuery{\n\t\t\tUpdateTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: func(*Column) { panic(ErrFaultySQL) },\n\t\t},\n\t}, {\n\t\tdescription: \"UpdateTable Policy err\",\n\t\titem: UpdateQuery{\n\t\t\tUpdateTable:  policyTableStub{err: ErrFaultySQL},\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"FromTable Policy err\",\n\t\titem: UpdateQuery{\n\t\t\tUpdateTable:  Expr(\"tbl\"),\n\t\t\tFromTable:    policyTableStub{err: ErrFaultySQL},\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"JoinTables Policy err\",\n\t\titem: UpdateQuery{\n\t\t\tUpdateTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t\tJoinTables: []JoinTable{\n\t\t\t\tJoin(policyTableStub{err: ErrFaultySQL}, Expr(\"1 = 1\")),\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"CTEs err\",\n\t\titem: UpdateQuery{\n\t\t\tCTEs:         []CTE{NewCTE(\"cte\", nil, Queryf(\"SELECT {}\", FaultySQL{}))},\n\t\t\tUpdateTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"UpdateTable err\",\n\t\titem: UpdateQuery{\n\t\t\tUpdateTable:  FaultySQL{},\n\t\t\tColumnMapper: colmapper,\n\t\t},\n\t}, {\n\t\tdescription: \"not mysql Assignments err\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:     DialectPostgres,\n\t\t\tUpdateTable: Expr(\"tbl\"),\n\t\t\tAssignments: []Assignment{FaultySQL{}},\n\t\t},\n\t}, {\n\t\tdescription: \"FromTable err\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tUpdateTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tFromTable:    FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"JoinTables err\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:      DialectPostgres,\n\t\t\tUpdateTable:  Expr(\"tbl\"),\n\t\t\tColumnMapper: colmapper,\n\t\t\tFromTable:    Expr(\"tbl\"),\n\t\t\tJoinTables: []JoinTable{\n\t\t\t\tJoin(FaultySQL{}, Expr(\"1 = 1\")),\n\t\t\t},\n\t\t},\n\t}, {\n\t\tdescription: \"mysql Assignments err\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:     DialectMySQL,\n\t\t\tUpdateTable: Expr(\"tbl\"),\n\t\t\tAssignments: []Assignment{FaultySQL{}},\n\t\t},\n\t}, {\n\t\tdescription: \"WherePredicate Variadic err\",\n\t\titem: UpdateQuery{\n\t\t\tUpdateTable:    Expr(\"tbl\"),\n\t\t\tColumnMapper:   colmapper,\n\t\t\tWherePredicate: And(FaultySQL{}),\n\t\t},\n\t}, {\n\t\tdescription: \"WherePredicate err\",\n\t\titem: UpdateQuery{\n\t\t\tUpdateTable:    Expr(\"tbl\"),\n\t\t\tColumnMapper:   colmapper,\n\t\t\tWherePredicate: FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"OrderByFields err\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:       DialectMySQL,\n\t\t\tUpdateTable:   Expr(\"tbl\"),\n\t\t\tColumnMapper:  colmapper,\n\t\t\tOrderByFields: Fields{FaultySQL{}},\n\t\t},\n\t}, {\n\t\tdescription: \"LimitRows err\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:       DialectMySQL,\n\t\t\tUpdateTable:   Expr(\"tbl\"),\n\t\t\tColumnMapper:  colmapper,\n\t\t\tOrderByFields: Fields{f1},\n\t\t\tLimitRows:     FaultySQL{},\n\t\t},\n\t}, {\n\t\tdescription: \"ReturningFields err\",\n\t\titem: UpdateQuery{\n\t\t\tDialect:         DialectPostgres,\n\t\t\tUpdateTable:     Expr(\"tbl\"),\n\t\t\tColumnMapper:    colmapper,\n\t\t\tReturningFields: Fields{FaultySQL{}},\n\t\t},\n\t}}\n\n\tfor _, tt := range errTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertErr(t, ErrFaultySQL)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "window.go",
    "content": "package sq\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n)\n\n// NamedWindow represents an SQL named window.\ntype NamedWindow struct {\n\tName       string\n\tDefinition Window\n}\n\nvar _ Window = (*NamedWindow)(nil)\n\n// WriteSQL implements the SQLWriter interface.\nfunc (w NamedWindow) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tbuf.WriteString(w.Name)\n\treturn nil\n}\n\n// IsWindow implements the Window interface.\nfunc (w NamedWindow) IsWindow() {}\n\n// WindowDefinition represents an SQL window definition.\ntype WindowDefinition struct {\n\tBaseWindowName    string\n\tPartitionByFields []Field\n\tOrderByFields     []Field\n\tFrameSpec         string\n\tFrameValues       []any\n}\n\nvar _ Window = (*WindowDefinition)(nil)\n\n// BaseWindow creates a new WindowDefinition based off an existing NamedWindow.\nfunc BaseWindow(w NamedWindow) WindowDefinition {\n\treturn WindowDefinition{BaseWindowName: w.Name}\n}\n\n// PartitionBy returns a new WindowDefinition with the PARTITION BY clause.\nfunc PartitionBy(fields ...Field) WindowDefinition {\n\treturn WindowDefinition{PartitionByFields: fields}\n}\n\n// PartitionBy returns a new WindowDefinition with the ORDER BY clause.\nfunc OrderBy(fields ...Field) WindowDefinition {\n\treturn WindowDefinition{OrderByFields: fields}\n}\n\n// WriteSQL implements the SQLWriter interface.\nfunc (w WindowDefinition) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\tvar written bool\n\tbuf.WriteString(\"(\")\n\tif w.BaseWindowName != \"\" {\n\t\tbuf.WriteString(w.BaseWindowName + \" \")\n\t}\n\tif len(w.PartitionByFields) > 0 {\n\t\twritten = true\n\t\tbuf.WriteString(\"PARTITION BY \")\n\t\terr = writeFields(ctx, dialect, buf, args, params, w.PartitionByFields, false)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Window PARTITION BY: %w\", err)\n\t\t}\n\t}\n\tif len(w.OrderByFields) > 0 {\n\t\tif written {\n\t\t\tbuf.WriteString(\" \")\n\t\t}\n\t\twritten = true\n\t\tbuf.WriteString(\"ORDER BY \")\n\t\terr = writeFields(ctx, dialect, buf, args, params, w.OrderByFields, false)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Window ORDER BY: %w\", err)\n\t\t}\n\t}\n\tif w.FrameSpec != \"\" {\n\t\tif written {\n\t\t\tbuf.WriteString(\" \")\n\t\t}\n\t\twritten = true\n\t\terr = Writef(ctx, dialect, buf, args, params, w.FrameSpec, w.FrameValues)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Window FRAME: %w\", err)\n\t\t}\n\t}\n\tbuf.WriteString(\")\")\n\treturn nil\n}\n\n// PartitionBy returns a new WindowDefinition with the PARTITION BY clause.\nfunc (w WindowDefinition) PartitionBy(fields ...Field) WindowDefinition {\n\tw.PartitionByFields = fields\n\treturn w\n}\n\n// OrderBy returns a new WindowDefinition with the ORDER BY clause.\nfunc (w WindowDefinition) OrderBy(fields ...Field) WindowDefinition {\n\tw.OrderByFields = fields\n\treturn w\n}\n\n// Frame returns a new WindowDefinition with the frame specification set.\nfunc (w WindowDefinition) Frame(frameSpec string, frameValues ...any) WindowDefinition {\n\tw.FrameSpec = frameSpec\n\tw.FrameValues = frameValues\n\treturn w\n}\n\n// IsWindow implements the Window interface.\nfunc (w WindowDefinition) IsWindow() {}\n\n// NamedWindows represents a slice of NamedWindows.\ntype NamedWindows []NamedWindow\n\n// WriteSQL imeplements the SQLWriter interface.\nfunc (ws NamedWindows) WriteSQL(ctx context.Context, dialect string, buf *bytes.Buffer, args *[]any, params map[string][]int) error {\n\tvar err error\n\tfor i, window := range ws {\n\t\tif i > 0 {\n\t\t\tbuf.WriteString(\", \")\n\t\t}\n\t\tbuf.WriteString(window.Name + \" AS \")\n\t\terr = window.Definition.WriteSQL(ctx, dialect, buf, args, params)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"window #%d: %w\", i+1, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// CountOver represents the COUNT(<field>) OVER (<window>) window function.\nfunc CountOver(field Field, window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"COUNT({}) OVER ()\", field)\n\t}\n\treturn Expr(\"COUNT({}) OVER {}\", field, window)\n}\n\n// CountStarOver represents the COUNT(*) OVER (<window>) window function.\nfunc CountStarOver(window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"COUNT(*) OVER ()\")\n\t}\n\treturn Expr(\"COUNT(*) OVER {}\", window)\n}\n\n// SumOver represents the SUM(<num>) OVER (<window>) window function.\nfunc SumOver(num Number, window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"SUM({}) OVER ()\", num)\n\t}\n\treturn Expr(\"SUM({}) OVER {}\", num, window)\n}\n\n// AvgOver represents the AVG(<num>) OVER (<window>) window function.\nfunc AvgOver(num Number, window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"AVG({}) OVER ()\", num)\n\t}\n\treturn Expr(\"AVG({}) OVER {}\", num, window)\n}\n\n// MinOver represents the MIN(<field>) OVER (<window>) window function.\nfunc MinOver(field Field, window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"MIN({}) OVER ()\", field)\n\t}\n\treturn Expr(\"MIN({}) OVER {}\", field, window)\n}\n\n// MaxOver represents the MAX(<field>) OVER (<window>) window function.\nfunc MaxOver(field Field, window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"MAX({}) OVER ()\", field)\n\t}\n\treturn Expr(\"MAX({}) OVER {}\", field, window)\n}\n\n// RowNumberOver represents the ROW_NUMBER() OVER (<window>) window function.\nfunc RowNumberOver(window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"ROW_NUMBER() OVER ()\")\n\t}\n\treturn Expr(\"ROW_NUMBER() OVER {}\", window)\n}\n\n// RankOver represents the RANK() OVER (<window>) window function.\nfunc RankOver(window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"RANK() OVER ()\")\n\t}\n\treturn Expr(\"RANK() OVER {}\", window)\n}\n\n// DenseRankOver represents the DENSE_RANK() OVER (<window>) window function.\nfunc DenseRankOver(window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"DENSE_RANK() OVER ()\")\n\t}\n\treturn Expr(\"DENSE_RANK() OVER {}\", window)\n}\n\n// CumeDistOver represents the CUME_DIST() OVER (<window>) window function.\nfunc CumeDistOver(window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"CUME_DIST() OVER ()\")\n\t}\n\treturn Expr(\"CUME_DIST() OVER {}\", window)\n}\n\n// FirstValueOver represents the FIRST_VALUE(<field>) OVER (<window>) window function.\nfunc FirstValueOver(field Field, window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"FIRST_VALUE({}) OVER ()\", field)\n\t}\n\treturn Expr(\"FIRST_VALUE({}) OVER {}\", field, window)\n}\n\n// LastValueOver represents the LAST_VALUE(<field>) OVER (<window>) window\n// function.\nfunc LastValueOver(field Field, window Window) Expression {\n\tif window == nil {\n\t\treturn Expr(\"LAST_VALUE({}) OVER ()\", field)\n\t}\n\treturn Expr(\"LAST_VALUE({}) OVER {}\", field, window)\n}\n"
  },
  {
    "path": "window_test.go",
    "content": "package sq\n\nimport \"testing\"\n\nfunc TestWindow(t *testing.T) {\n\tt.Run(\"basic\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tf1, f2, f3 := Expr(\"f1\"), Expr(\"f2\"), Expr(\"f3\")\n\t\tTestTable{\n\t\t\titem:      PartitionBy(f1).OrderBy(f2, f3).Frame(\"RANGE UNBOUNDED PRECEDING\"),\n\t\t\twantQuery: \"(PARTITION BY f1 ORDER BY f2, f3 RANGE UNBOUNDED PRECEDING)\",\n\t\t}.assert(t)\n\t\tTestTable{\n\t\t\titem:      OrderBy(f1).PartitionBy(f2, f3).Frame(\"ROWS {} PRECEDING\", 5),\n\t\t\twantQuery: \"(PARTITION BY f2, f3 ORDER BY f1 ROWS ? PRECEDING)\",\n\t\t\twantArgs:  []any{5},\n\t\t}.assert(t)\n\t})\n\n\terrTests := []TestTable{{\n\t\tdescription: \"PartitionBy err\", item: PartitionBy(FaultySQL{}),\n\t}, {\n\t\tdescription: \"OrderBy err\", item: OrderBy(FaultySQL{}),\n\t}, {\n\t\tdescription: \"Frame err\", item: OrderBy(Expr(\"f\")).Frame(\"ROWS {} PRECEDING\", FaultySQL{}),\n\t}, {\n\t\tdescription: \"NamedWindows err\", item: NamedWindows{{\n\t\t\tName:       \"w\",\n\t\t\tDefinition: OrderBy(Expr(\"f\")).Frame(\"ROWS {} PRECEDING\", FaultySQL{}),\n\t\t}},\n\t}}\n\n\tfor _, tt := range errTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assertErr(t, ErrFaultySQL)\n\t\t})\n\t}\n\n\tfuncTests := []TestTable{{\n\t\tdescription: \"CountOver\", item: CountOver(Expr(\"f1\"), WindowDefinition{}),\n\t\twantQuery: \"COUNT(f1) OVER ()\",\n\t}, {\n\t\tdescription: \"CountOver nil\", item: CountOver(Expr(\"f1\"), nil),\n\t\twantQuery: \"COUNT(f1) OVER ()\",\n\t}, {\n\t\tdescription: \"CountStarOver\", item: CountStarOver(WindowDefinition{}),\n\t\twantQuery: \"COUNT(*) OVER ()\",\n\t}, {\n\t\tdescription: \"SumOver\", item: SumOver(Expr(\"f1\"), PartitionBy(Expr(\"f2\"))),\n\t\twantQuery: \"SUM(f1) OVER (PARTITION BY f2)\",\n\t}, {\n\t\tdescription: \"AvgOver\", item: AvgOver(Expr(\"f1\"), PartitionBy(Expr(\"f2\"))),\n\t\twantQuery: \"AVG(f1) OVER (PARTITION BY f2)\",\n\t}, {\n\t\tdescription: \"MinOver\", item: MinOver(Expr(\"f1\"), PartitionBy(Expr(\"f2\"))),\n\t\twantQuery: \"MIN(f1) OVER (PARTITION BY f2)\",\n\t}, {\n\t\tdescription: \"MaxOver\", item: MaxOver(Expr(\"f1\"), PartitionBy(Expr(\"f2\"))),\n\t\twantQuery: \"MAX(f1) OVER (PARTITION BY f2)\",\n\t}, {\n\t\tdescription: \"RowNumberOver\", item: RowNumberOver(PartitionBy(Expr(\"f1\"))),\n\t\twantQuery: \"ROW_NUMBER() OVER (PARTITION BY f1)\",\n\t}, {\n\t\tdescription: \"RankOver\", item: RankOver(PartitionBy(Expr(\"f1\"))),\n\t\twantQuery: \"RANK() OVER (PARTITION BY f1)\",\n\t}, {\n\t\tdescription: \"DenseRankOver\", item: DenseRankOver(PartitionBy(Expr(\"f1\"))),\n\t\twantQuery: \"DENSE_RANK() OVER (PARTITION BY f1)\",\n\t}, {\n\t\tdescription: \"CumeDistOver\", item: CumeDistOver(PartitionBy(Expr(\"f1\"))),\n\t\twantQuery: \"CUME_DIST() OVER (PARTITION BY f1)\",\n\t}, {\n\t\tdescription: \"FirstValueOver\", item: FirstValueOver(Expr(\"f1\"), PartitionBy(Expr(\"f2\"))),\n\t\twantQuery: \"FIRST_VALUE(f1) OVER (PARTITION BY f2)\",\n\t}, {\n\t\tdescription: \"LastValueOver\", item: LastValueOver(Expr(\"f1\"), PartitionBy(Expr(\"f2\"))),\n\t\twantQuery: \"LAST_VALUE(f1) OVER (PARTITION BY f2)\",\n\t}, {\n\t\tdescription: \"NamedWindow\", item: CountStarOver(NamedWindow{Name: \"w\"}),\n\t\twantQuery: \"COUNT(*) OVER w\",\n\t}, func() TestTable {\n\t\tvar tt TestTable\n\t\ttt.description = \"BaseWindow\"\n\t\tw := NamedWindow{Name: \"w\", Definition: PartitionBy(Expr(\"f1\"))}\n\t\ttt.item = CountStarOver(BaseWindow(w).Frame(\"ROWS UNBOUNDED PRECEDING\"))\n\t\ttt.wantQuery = \"COUNT(*) OVER (w ROWS UNBOUNDED PRECEDING)\"\n\t\treturn tt\n\t}(), func() TestTable {\n\t\tvar tt TestTable\n\t\ttt.description = \"NamedWindows\"\n\t\tw1 := NamedWindow{Name: \"w1\", Definition: PartitionBy(Expr(\"f1\"))}\n\t\tw2 := NamedWindow{Name: \"w2\", Definition: OrderBy(Expr(\"f2\"))}\n\t\tw3 := NamedWindow{Name: \"w3\", Definition: OrderBy(Expr(\"f3\")).Frame(\"ROWS UNBOUNDED PRECEDING\")}\n\t\ttt.item = NamedWindows{w1, w2, w3}\n\t\ttt.wantQuery = \"w1 AS (PARTITION BY f1)\" +\n\t\t\t\", w2 AS (ORDER BY f2)\" +\n\t\t\t\", w3 AS (ORDER BY f3 ROWS UNBOUNDED PRECEDING)\"\n\t\treturn tt\n\t}()}\n\n\tfor _, tt := range funcTests {\n\t\ttt := tt\n\t\tt.Run(tt.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\ttt.assert(t)\n\t\t})\n\t}\n}\n"
  }
]