{"id":19500062,"url":"https://github.com/prestodb/presto-go-client","last_synced_at":"2026-04-01T18:31:47.488Z","repository":{"id":27398972,"uuid":"102803969","full_name":"prestodb/presto-go-client","owner":"prestodb","description":"A Presto client for the Go programming language.","archived":false,"fork":false,"pushed_at":"2026-03-22T21:48:04.000Z","size":261,"stargazers_count":230,"open_issues_count":1,"forks_count":60,"subscribers_count":28,"default_branch":"main","last_synced_at":"2026-03-23T14:02:04.183Z","etag":null,"topics":["big-data","golang","presto","prestodb","sql"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prestodb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-09-08T01:43:37.000Z","updated_at":"2026-03-22T21:48:06.000Z","dependencies_parsed_at":"2024-03-06T17:17:09.163Z","dependency_job_id":"0a03fb8d-fff9-4792-b08a-2168edd66600","html_url":"https://github.com/prestodb/presto-go-client","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/prestodb/presto-go-client","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prestodb%2Fpresto-go-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prestodb%2Fpresto-go-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prestodb%2Fpresto-go-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prestodb%2Fpresto-go-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prestodb","download_url":"https://codeload.github.com/prestodb/presto-go-client/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prestodb%2Fpresto-go-client/sbom","scorecard":{"id":744699,"data":{"date":"2025-08-11","repo":{"name":"github.com/prestodb/presto-go-client","commit":"905ac40a178381015fd789a4c8999b60596cc524"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":9,"reason":"Found 22/23 approved changesets -- score normalized to 9","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: containerImage not pinned by hash: integration_tests/Dockerfile:1: pin your Docker image by updating openjdk:8-jre to openjdk:8-jre@sha256:667a15e7bc533a90fb39ddb7e5bed63162ac3c13a97e6c698bf4f139f51b7d33","Info:   0 out of   1 containerImage dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":3,"reason":"7 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2021-0227 / GHSA-3vm4-22fp-5rfm","Warn: Project is vulnerable to: GO-2022-0968 / GHSA-gwc9-m7rh-j2ww","Warn: Project is vulnerable to: GO-2021-0356 / GHSA-8c26-wmh5-6g9v","Warn: Project is vulnerable to: GO-2024-2961","Warn: Project is vulnerable to: GO-2023-2402 / GHSA-45x7-px36-x8w8","Warn: Project is vulnerable to: GO-2024-3321 / GHSA-v778-237x-gjrc","Warn: Project is vulnerable to: GO-2025-3487 / GHSA-hcg3-q754-cr77"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-22T18:23:24.210Z","repository_id":27398972,"created_at":"2025-08-22T18:23:24.210Z","updated_at":"2025-08-22T18:23:24.210Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290875,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["big-data","golang","presto","prestodb","sql"],"created_at":"2024-11-10T22:07:22.142Z","updated_at":"2026-04-01T18:31:47.483Z","avatar_url":"https://github.com/prestodb.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# presto-go\n\nA Go client library for [Presto](https://prestodb.io/) and [Trino](https://trino.io/) SQL query engines.\n\n## Features\n\n- **`database/sql` driver** — use the standard Go database API (`sql.Open`, `db.Query`, `rows.Scan`)\n- Complete Presto REST API client (query execution, cluster info, query state, query info)\n- Query info/stats parsing (`queryjson` subpackage)\n- Trino compatibility mode (automatic header translation)\n- Session management with isolated, cloneable sessions\n- Transaction state tracking (automatic via response headers)\n- Batch result streaming with memory-efficient `Drain` API\n- Automatic retry with exponential backoff on 503 responses and transient connection errors\n- Gzip request/response compression\n- Thread-safe concurrent session access\n- Fluent API for session configuration\n- Pre-minted query ID support\n- TLS/SSL with custom CAs and mutual TLS\n- Kerberos/SPNEGO authentication (opt-in separate module)\n- OAuth2/JWT authentication (opt-in separate module)\n- Generic complex type scanners (`NullSlice[T]`, `NullMap[K,V]`, `NullRow[T]`)\n\n## Installation\n\n```bash\ngo get github.com/prestodb/presto-go-client/v2\n```\n\nOptional authentication modules (separate dependencies, opt-in):\n\n```bash\ngo get github.com/prestodb/presto-go-client/v2/prestoauth/kerberos  # Kerberos/SPNEGO\ngo get github.com/prestodb/presto-go-client/v2/prestoauth/oauth2    # OAuth2/JWT\n```\n\n## Quick Start\n\n### Using `database/sql` (recommended)\n\n```go\npackage main\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\n\t_ \"github.com/prestodb/presto-go-client/v2\" // registers \"presto\" driver\n)\n\nfunc main() {\n\tdb, err := sql.Open(\"presto\", \"presto://localhost:8080/hive/default\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer db.Close()\n\n\trows, err := db.Query(\"SELECT id, name FROM users WHERE active = ?\", true)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tvar id int64\n\t\tvar name string\n\t\tif err := rows.Scan(\u0026id, \u0026name); err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tfmt.Println(id, name)\n\t}\n}\n```\n\n### Using the low-level API\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/prestodb/presto-go-client/v2\"\n)\n\nfunc main() {\n\tclient, err := presto.NewClient(\"http://localhost:8080\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tsession := client.NewSession()\n\tsession.Catalog(\"hive\").Schema(\"default\").User(\"analyst\")\n\n\tctx := context.Background()\n\tresults, _, err := session.Query(ctx, \"SELECT id, name FROM users LIMIT 10\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\terr = results.Drain(ctx, func(qr *presto.QueryResults) error {\n\t\tfor _, row := range qr.Data {\n\t\t\tvar parsed []any\n\t\t\tif err := json.Unmarshal(row, \u0026parsed); err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\tfmt.Println(parsed)\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n## Usage\n\n### `database/sql` Driver\n\n#### DSN Format\n\n```\npresto://[user[:password]@]host[:port][/catalog[/schema]][?key=value\u0026...]\ntrino://...   (enables Trino header mode)\n```\n\nDefault port is 8080 for both schemes. Query parameters:\n\n| Parameter | Description |\n|-----------|-------------|\n| `timezone` | Session time zone |\n| `client_tags` | Comma-separated tags |\n| `client_info` | Client info string |\n| `source` | Query source identifier |\n| `ssl_cert` | Path to client certificate (PEM) for mutual TLS |\n| `ssl_key` | Path to client private key (PEM) |\n| `ssl_ca` | Path to CA certificate (PEM) for custom CAs |\n| `ssl_skip_verify` | Skip TLS certificate verification (`true`/`1`) |\n| *(other)* | Set as session properties |\n\nWhen any `ssl_*` parameter is set, the connection automatically upgrades to HTTPS.\n\n#### Using `sql.OpenDB` with a Connector\n\n```go\nconnector, err := presto.NewConnector(\"presto://user@host:8080/hive/default\")\nif err != nil {\n    log.Fatal(err)\n}\ndb := sql.OpenDB(connector)\n```\n\n#### Parameter Interpolation\n\nThe driver interpolates `?` placeholders client-side into SQL literals:\n\n```go\nrows, err := db.Query(\"SELECT * FROM t WHERE name = ? AND id = ?\", \"alice\", 42)\n```\n\n#### Transactions\n\n```go\ntx, err := db.BeginTx(ctx, nil)\n// ... use tx.Query / tx.Exec ...\ntx.Commit() // or tx.Rollback()\n```\n\nAll Presto isolation levels are supported via `sql.TxOptions`:\n\n```go\ntx, err := db.BeginTx(ctx, \u0026sql.TxOptions{\n    Isolation: sql.LevelSerializable,\n    ReadOnly:  true,\n})\n```\n\n### Client Initialization\n\n```go\n// Basic client\nclient, err := presto.NewClient(\"http://presto-coordinator:8080\")\n\n// With basic auth\nclient, err := presto.NewClient(\"http://presto-coordinator:8080\", \"base64-encoded-credentials\")\n\n// Trino mode with HTTPS\nclient, err := presto.NewClient(\"http://trino-coordinator:8443\")\nclient.IsTrino(true).ForceHTTPS(true)\n```\n\n### TLS Configuration\n\n#### Via DSN (database/sql)\n\n```go\ndb, _ := sql.Open(\"presto\", \"presto://host:8443/catalog?ssl_ca=/path/ca.pem\")\n// or with mutual TLS:\ndb, _ := sql.Open(\"presto\", \"presto://host:8443/catalog?ssl_cert=/path/cert.pem\u0026ssl_key=/path/key.pem\u0026ssl_ca=/path/ca.pem\")\n// or skip verification (development only):\ndb, _ := sql.Open(\"presto\", \"presto://host:8443/catalog?ssl_skip_verify=true\")\n```\n\n#### Via low-level API\n\n```go\nclient, _ := presto.NewClient(\"https://presto:8443\")\n\n// Use BuildTLSConfig helper (same logic as DSN parsing):\ntlsCfg, _ := presto.BuildTLSConfig(\"/path/ca.pem\", \"\", \"\", false)\nclient.TLSConfig(tlsCfg)\n\n// or with mutual TLS:\ntlsCfg, _ = presto.BuildTLSConfig(\"/path/ca.pem\", \"/path/cert.pem\", \"/path/key.pem\", false)\nclient.TLSConfig(tlsCfg)\n\n// or provide a fully custom http.Client:\nclient.HTTPClient(\u0026http.Client{\n    Transport: \u0026http.Transport{TLSClientConfig: tlsCfg},\n})\n```\n\n#### Via Connector Options\n\n```go\nconnector, _ := presto.NewConnector(\"presto://host:8443/catalog\",\n    presto.WithHTTPClient(customHTTPClient),\n)\n```\n\n### Session Management\n\nSessions provide isolated execution contexts. Each session maintains its own catalog, schema, user identity, transaction state, and session parameters.\n\n```go\n// Create an isolated session from the client\nsession := client.NewSession()\nsession.Catalog(\"hive\").Schema(\"production\").User(\"etl_user\")\n\n// Set session parameters\nsession.SessionParam(\"query_max_memory\", \"2GB\")\nsession.SessionParam(\"join_distribution_type\", \"PARTITIONED\")\n\n// Remove a session parameter\nsession.SessionParam(\"join_distribution_type\", nil)\n\n// Clone a session for parallel workloads\ns2 := session.Clone()\ns2.Schema(\"staging\")\n```\n\nThe client also processes `SET SESSION` and `RESET SESSION` SQL statements automatically. When the server responds with `X-Presto-Set-Session` or `X-Presto-Clear-Session` headers, the session properties are updated for subsequent requests. However, it is recommended to use the `SessionParam()` API or DSN parameters instead of executing `SET SESSION` / `RESET SESSION` queries.\n\n### Query Execution\n\n```go\n// Simple query\nresults, _, err := session.Query(ctx, \"SELECT * FROM orders WHERE status = 'pending'\")\n\n// Query with pre-minted ID (for tracking)\nresults, _, err := session.QueryWithPreMintedID(ctx, \"SELECT 1\", \"custom-query-id\", \"slug\")\n\n// Manual batch fetching — note: results.Data may already contain rows\n// from the initial response, so process it before entering the loop.\nfor results.HasMoreBatch() {\n    err := results.FetchNextBatch(ctx)\n    if err != nil {\n        log.Fatal(err)\n    }\n    // Process results.Data\n}\n\n// Streaming drain (memory-efficient for large result sets)\nerr = results.Drain(ctx, func(qr *presto.QueryResults) error {\n    // Process each batch; data is cleared after handler returns\n    fmt.Printf(\"Batch: %d rows\\n\", len(qr.Data))\n    return nil\n})\n```\n\n### REST API Endpoints\n\n#### Cluster Info\n\n```go\nstats, _, err := session.GetClusterInfo(ctx)\nfmt.Printf(\"Active workers: %d, Running queries: %d\\n\", stats.ActiveWorkers, stats.RunningQueries)\n```\n\n#### Query State\n\n```go\n// List all queries for a specific user\nuser := \"analyst\"\nstates, _, err := session.GetQueryState(ctx, \u0026presto.GetQueryStateOptions{\n    User: \u0026user,\n})\nfor _, s := range states {\n    fmt.Printf(\"Query %s: %s\\n\", s.QueryId, s.QueryState)\n}\n```\n\n#### Query Info\n\n```go\n// Decode into a struct\nvar info queryjson.QueryInfo\n_, err := session.GetQueryInfo(ctx, \"20231001_123456_00001_xxxxx\", \u0026info)\n\n// Or write raw JSON to a file\nfile, _ := os.Create(\"query.json\")\n_, err = session.GetQueryInfo(ctx, queryId, file)\n```\n\n### Cancellation\n\nContext cancellation automatically triggers server-side query cleanup:\n\n```go\nctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer cancel()\n\nresults, _, err := session.Query(ctx, \"SELECT * FROM large_table\")\n// If context times out during FetchNextBatch, the query is canceled on the server\n```\n\n### Request Options\n\nOverride individual request settings without modifying the session:\n\n```go\nopt := func(r *http.Request) {\n    r.Header.Set(\"X-Custom-Header\", \"value\")\n}\nresults, _, err := session.Query(ctx, \"SELECT 1\", opt)\n```\n\n### Persistent Request Options\n\nSet request options that apply to every request from a session, including batch fetches. This is essential for authentication schemes like Kerberos/SPNEGO:\n\n```go\nsession.RequestOptions(func(r *http.Request) {\n    r.Header.Set(\"Authorization\", \"Bearer my-token\")\n})\nresults, _, err := session.Query(ctx, \"SELECT 1\") // header applied\n// header also applied to all FetchNextBatch calls\n```\n\nPer-call options override session-level options when both set the same header.\n\n### Kerberos/SPNEGO Authentication\n\nThe `prestoauth/kerberos` module provides Kerberos authentication as a separate module to keep the `gokrb5` dependency tree opt-in.\n\n#### Using `database/sql`\n\n```go\nimport (\n    \"database/sql\"\n    \"github.com/prestodb/presto-go-client/v2/prestoauth/kerberos\"\n)\n\nconnector, closer, err := kerberos.NewConnector(\n    \"presto://host:8080/catalog/schema?kerberos_keytab=/etc/presto.keytab\u0026kerberos_principal=user@REALM\u0026kerberos_realm=REALM\u0026kerberos_config=/etc/krb5.conf\",\n)\nif err != nil {\n    log.Fatal(err)\n}\ndefer closer.Close()\n\ndb := sql.OpenDB(connector)\ndb.Query(\"SELECT 1\") // SPNEGO header applied to all requests\n```\n\nDSN parameters for Kerberos:\n\n| Parameter | Description |\n|-----------|-------------|\n| `kerberos_keytab` | Path to the `.keytab` file |\n| `kerberos_principal` | Kerberos principal (e.g. `user@EXAMPLE.COM`) |\n| `kerberos_realm` | Kerberos realm (e.g. `EXAMPLE.COM`) |\n| `kerberos_config` | Path to `krb5.conf` |\n| `kerberos_service_spn` | Service principal name (defaults to `HTTP/\u003chostname\u003e`) |\n\n#### Using the low-level API\n\n```go\nimport (\n    \"github.com/prestodb/presto-go-client/v2\"\n    \"github.com/prestodb/presto-go-client/v2/prestoauth/kerberos\"\n)\n\nclient, _ := presto.NewClient(\"http://presto:8080\")\nsession := client.NewSession()\n\nkrbOpt, closer, err := kerberos.NewRequestOption(kerberos.Config{\n    KeytabPath: \"/etc/presto.keytab\",\n    Principal:  \"presto/host@EXAMPLE.COM\",\n    Realm:      \"EXAMPLE.COM\",\n    ConfigPath: \"/etc/krb5.conf\",\n})\nif err != nil {\n    log.Fatal(err)\n}\ndefer closer.Close()\n\nsession.RequestOptions(krbOpt)\nresults, _, _ := session.Query(ctx, \"SELECT 1\") // SPNEGO header applied\n```\n\n### OAuth2/JWT Authentication\n\nThe `prestoauth/oauth2` module provides token-based authentication as a separate module.\n\n#### Static Bearer Token\n\n```go\nimport \"github.com/prestodb/presto-go-client/v2/prestoauth/oauth2\"\n\n// Via DSN\nconnector, _ := oauth2.NewConnector(\"presto://host:8080/catalog?access_token=my-jwt-token\")\ndb := sql.OpenDB(connector)\n\n// Via low-level API\nsession.RequestOptions(oauth2.NewStaticTokenOption(\"my-jwt-token\"))\n```\n\n#### OAuth2 Client Credentials Flow\n\nTokens are automatically obtained and refreshed:\n\n```go\n// Via DSN\nconnector, _ := oauth2.NewConnector(\n    \"presto://host:8080/catalog?oauth2_client_id=id\u0026oauth2_client_secret=secret\u0026oauth2_token_url=https://auth.example.com/token\u0026oauth2_scopes=read,write\",\n)\ndb := sql.OpenDB(connector)\n\n// Via low-level API\nopt, _ := oauth2.NewRequestOption(oauth2.Config{\n    ClientID:     \"my-client\",\n    ClientSecret: \"my-secret\",\n    TokenURL:     \"https://auth.example.com/token\",\n    Scopes:       []string{\"read\", \"write\"},\n})\nsession.RequestOptions(opt)\n```\n\nDSN parameters for OAuth2:\n\n| Parameter | Description |\n|-----------|-------------|\n| `access_token` | Static Bearer token |\n| `oauth2_client_id` | OAuth2 client ID |\n| `oauth2_client_secret` | OAuth2 client secret |\n| `oauth2_token_url` | Token endpoint URL |\n| `oauth2_scopes` | Comma-separated scopes |\n\n### Complex Type Scanning\n\nPresto `ARRAY`, `MAP`, and `ROW` columns are returned as JSON strings through `database/sql`. Use the provided generic scanner types to deserialize them:\n\n```go\n// ARRAY columns\nvar tags presto.NullSlice[string]\nrow.Scan(\u0026tags)\nfmt.Println(tags.Slice) // []string{\"go\", \"presto\"}\n\n// MAP columns\nvar props presto.NullMap[string, float64]\nrow.Scan(\u0026props)\nfmt.Println(props.Map) // map[string]float64{\"timeout\": 30}\n\n// ROW columns (scan into a struct)\ntype Address struct {\n    Street string `json:\"street\"`\n    City   string `json:\"city\"`\n}\nvar addr presto.NullRow[Address]\nrow.Scan(\u0026addr)\nfmt.Println(addr.Row.Street) // \"123 Main St\"\n```\n\nAll three types are nullable (`Valid` field), implement `sql.Scanner` and `driver.Valuer`, and support any JSON-compatible type parameter.\n\n### Interval Types\n\n`INTERVAL DAY TO SECOND` columns are scanned as `time.Duration`. `INTERVAL YEAR TO MONTH` columns are scanned as `string` (in Presto's `\"Y-M\"` format, e.g. `\"3-6\"` for 3 years 6 months) since months and years are not fixed-length durations.\n\nWhen passing parameters, `time.Duration` values are interpolated as `INTERVAL '...' DAY TO SECOND`:\n\n```go\nrows, err := db.QueryContext(ctx, \"SELECT date_add('day', ?, now())\", 2*24*time.Hour+6*time.Hour)\n```\n\n### Connector Options\n\n`NewConnector` accepts variadic options for configuring sessions created by the connector:\n\n```go\nconnector, err := presto.NewConnector(\"presto://host:8080/hive/default\",\n    presto.WithSessionSetup(func(s *presto.Session) {\n        s.RequestOptions(myAuthOption)\n    }),\n    presto.WithHTTPClient(customHTTPClient),\n)\ndb := sql.OpenDB(connector)\n```\n\n## Testing\n\n```bash\ngo test ./... -v\n```\n\n### Examples\n\nThe `example_test.go` file contains runnable getting-started snippets covering both the `database/sql` interface and the low-level API. All examples are skipped by default since they require a live Presto/Trino server:\n\n```bash\ngo test -run TestExample -v\n```\n\n### Mock Server\n\nThe `prestotest` package provides a `MockPrestoServer` for integration testing. It uses only the standard library (`net/http`), so it introduces no additional dependencies.\n\n```go\nimport (\n    \"github.com/prestodb/presto-go-client/v2\"\n    \"github.com/prestodb/presto-go-client/v2/prestotest\"\n)\n\nfunc TestMyApp(t *testing.T) {\n    mock := prestotest.NewMockPrestoServer()\n    defer mock.Close()\n\n    mock.AddQuery(\u0026prestotest.MockQueryTemplate{\n        SQL:         \"SELECT * FROM users\",\n        Columns:     []presto.Column{{Name: \"id\", Type: \"bigint\"}},\n        Data:        [][]any{{1}, {2}, {3}},\n        DataBatches: 2,\n    })\n\n    client, _ := presto.NewClient(mock.URL())\n    session := client.NewSession()\n\n    results, _, err := session.Query(context.Background(), \"SELECT * FROM users\")\n    // Assert on results...\n}\n```\n\n## License\n\nApache License 2.0. See [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprestodb%2Fpresto-go-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprestodb%2Fpresto-go-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprestodb%2Fpresto-go-client/lists"}