[{"data":1,"prerenderedAt":2377},["ShallowReactive",2],{"navigation_docs_en":3,"-en-database-pgx":342,"-en-database-pgx-surround":2372},[4,45,61,96,132,166,196,217,241,265],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F10.reference",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F10.reference\u002F1.configuration","i-lucide-settings",{"title":57,"path":58,"stem":59,"icon":60},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F10.reference\u002F2.dependencies","i-lucide-package",{"title":62,"icon":63,"path":64,"stem":65,"children":66,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[67,72,77,82,86,91],{"title":68,"path":69,"stem":70,"icon":71},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture","i-lucide-layers",{"title":73,"path":74,"stem":75,"icon":76},"Application lifecycle","\u002Fen\u002Fconcepts\u002Fapplication-lifecycle","en\u002F2.concepts\u002F2.application-lifecycle","i-lucide-power",{"title":78,"path":79,"stem":80,"icon":81},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F3.error-model","i-lucide-shield-alert",{"title":52,"path":83,"stem":84,"icon":85},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F4.configuration","i-lucide-settings-2",{"title":87,"path":88,"stem":89,"icon":90},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F5.codegen-pipeline","i-lucide-file-json",{"title":92,"path":93,"stem":94,"icon":95},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F6.design-patterns","i-lucide-puzzle",{"title":97,"icon":98,"path":99,"stem":100,"children":101,"page":44},"HTTP & Routing","i-lucide-globe","\u002Fen\u002Fhttp","en\u002F3.http",[102,107,112,117,122,127],{"title":103,"path":104,"stem":105,"icon":106},"The server core","\u002Fen\u002Fhttp\u002Fserver","en\u002F3.http\u002F1.server","i-lucide-server",{"title":108,"path":109,"stem":110,"icon":111},"The Fiber engine","\u002Fen\u002Fhttp\u002Ffiber","en\u002F3.http\u002F2.fiber","i-lucide-zap",{"title":113,"path":114,"stem":115,"icon":116},"The chi engine","\u002Fen\u002Fhttp\u002Fchi","en\u002F3.http\u002F3.chi","i-lucide-route",{"title":118,"path":119,"stem":120,"icon":121},"Error responses","\u002Fen\u002Fhttp\u002Ferror-responses","en\u002F3.http\u002F4.error-responses","i-lucide-octagon-alert",{"title":123,"path":124,"stem":125,"icon":126},"Validation","\u002Fen\u002Fhttp\u002Fvalidation","en\u002F3.http\u002F5.validation","i-lucide-badge-check",{"title":128,"path":129,"stem":130,"icon":131},"Pagination","\u002Fen\u002Fhttp\u002Fpagination","en\u002F3.http\u002F6.pagination","i-lucide-list-ordered",{"title":133,"icon":134,"path":135,"stem":136,"children":137,"page":44},"Database","i-lucide-database","\u002Fen\u002Fdatabase","en\u002F4.database",[138,142,147,151,156,161],{"title":139,"path":140,"stem":141,"icon":134},"Overview","\u002Fen\u002Fdatabase\u002Foverview","en\u002F4.database\u002F1.overview",{"title":143,"path":144,"stem":145,"icon":146},"pgx","\u002Fen\u002Fdatabase\u002Fpgx","en\u002F4.database\u002F2.pgx","i-lucide-plug",{"title":148,"path":149,"stem":150,"icon":71},"bun","\u002Fen\u002Fdatabase\u002Fbun","en\u002F4.database\u002F3.bun",{"title":152,"path":153,"stem":154,"icon":155},"Transactions","\u002Fen\u002Fdatabase\u002Ftransactions","en\u002F4.database\u002F4.transactions","i-lucide-git-merge",{"title":157,"path":158,"stem":159,"icon":160},"Migrations","\u002Fen\u002Fdatabase\u002Fmigrations","en\u002F4.database\u002F5.migrations","i-lucide-file-stack",{"title":162,"path":163,"stem":164,"icon":165},"Seeders","\u002Fen\u002Fdatabase\u002Fseeding","en\u002F4.database\u002F6.seeding","i-lucide-sprout",{"title":167,"icon":168,"path":169,"stem":170,"children":171,"page":44},"Async & Background","i-lucide-workflow","\u002Fen\u002Fasync","en\u002F5.async",[172,176,181,186,191],{"title":173,"path":174,"stem":175,"icon":131},"Queue","\u002Fen\u002Fasync\u002Fqueue","en\u002F5.async\u002F1.queue",{"title":177,"path":178,"stem":179,"icon":180},"Events","\u002Fen\u002Fasync\u002Fevents","en\u002F5.async\u002F2.events","i-lucide-radio",{"title":182,"path":183,"stem":184,"icon":185},"Outbox","\u002Fen\u002Fasync\u002Foutbox","en\u002F5.async\u002F3.outbox","i-lucide-inbox",{"title":187,"path":188,"stem":189,"icon":190},"Scheduling","\u002Fen\u002Fasync\u002Fscheduler","en\u002F5.async\u002F4.scheduler","i-lucide-calendar-clock",{"title":192,"path":193,"stem":194,"icon":195},"Worker","\u002Fen\u002Fasync\u002Fworker","en\u002F5.async\u002F5.worker","i-lucide-cog",{"title":197,"icon":198,"path":199,"stem":200,"children":201,"page":44},"Security","i-lucide-shield-check","\u002Fen\u002Fsecurity","en\u002F6.security",[202,207,212],{"title":203,"path":204,"stem":205,"icon":206},"Authentication","\u002Fen\u002Fsecurity\u002Fauthentication","en\u002F6.security\u002F1.authentication","i-lucide-key-round",{"title":208,"path":209,"stem":210,"icon":211},"RBAC","\u002Fen\u002Fsecurity\u002Frbac","en\u002F6.security\u002F2.rbac","i-lucide-users",{"title":213,"path":214,"stem":215,"icon":216},"Policies","\u002Fen\u002Fsecurity\u002Fpolicies","en\u002F6.security\u002F3.policies","i-lucide-gavel",{"title":218,"icon":219,"path":220,"stem":221,"children":222,"page":44},"Observability","i-lucide-activity","\u002Fen\u002Fobservability","en\u002F7.observability",[223,226,231,236],{"title":139,"path":224,"stem":225,"icon":219},"\u002Fen\u002Fobservability\u002Foverview","en\u002F7.observability\u002F1.overview",{"title":227,"path":228,"stem":229,"icon":230},"Logging","\u002Fen\u002Fobservability\u002Flogging","en\u002F7.observability\u002F2.logging","i-lucide-scroll-text",{"title":232,"path":233,"stem":234,"icon":235},"OpenTelemetry","\u002Fen\u002Fobservability\u002Fopentelemetry","en\u002F7.observability\u002F3.opentelemetry","i-lucide-radar",{"title":237,"path":238,"stem":239,"icon":240},"Sentry","\u002Fen\u002Fobservability\u002Fsentry","en\u002F7.observability\u002F4.sentry","i-lucide-bug",{"title":242,"icon":243,"path":244,"stem":245,"children":246,"page":44},"Storage & Mail","i-lucide-hard-drive","\u002Fen\u002Fstorage-mail","en\u002F8.storage-mail",[247,251,256,261],{"title":248,"path":249,"stem":250,"icon":243},"Storage","\u002Fen\u002Fstorage-mail\u002Fstorage","en\u002F8.storage-mail\u002F1.storage",{"title":252,"path":253,"stem":254,"icon":255},"Email","\u002Fen\u002Fstorage-mail\u002Fmail","en\u002F8.storage-mail\u002F2.mail","i-lucide-mail",{"title":257,"path":258,"stem":259,"icon":260},"Notifications","\u002Fen\u002Fstorage-mail\u002Fnotifications","en\u002F8.storage-mail\u002F3.notifications","i-lucide-bell",{"title":262,"path":263,"stem":264,"icon":180},"Realtime","\u002Fen\u002Fstorage-mail\u002Frealtime","en\u002F8.storage-mail\u002F4.realtime",{"title":266,"icon":267,"path":268,"stem":269,"children":270,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F9.cli",[271,274,279,284,289,294,299,303,308,313,318,323,328,333,337],{"title":139,"path":272,"stem":273,"icon":267},"\u002Fen\u002Fcli\u002Foverview","en\u002F9.cli\u002F1.overview",{"title":275,"path":276,"stem":277,"icon":278},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F9.cli\u002F10.add-db","i-lucide-database-zap",{"title":280,"path":281,"stem":282,"icon":283},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F9.cli\u002F11.add-compose","i-lucide-container",{"title":285,"path":286,"stem":287,"icon":288},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F9.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":290,"path":291,"stem":292,"icon":293},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F9.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":295,"path":296,"stem":297,"icon":298},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F9.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":300,"path":301,"stem":302,"icon":165},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F9.cli\u002F15.add-seeder",{"title":304,"path":305,"stem":306,"icon":307},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F9.cli\u002F2.new-project","i-lucide-folder-plus",{"title":309,"path":310,"stem":311,"icon":312},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F9.cli\u002F3.new-module","i-lucide-blocks",{"title":314,"path":315,"stem":316,"icon":317},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F9.cli\u002F4.add-surface","i-lucide-layers-2",{"title":319,"path":320,"stem":321,"icon":322},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F9.cli\u002F5.add-core","i-lucide-box",{"title":324,"path":325,"stem":326,"icon":327},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F9.cli\u002F6.add-handler","i-lucide-webhook",{"title":329,"path":330,"stem":331,"icon":332},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F9.cli\u002F7.new-migration","i-lucide-file-plus",{"title":334,"path":335,"stem":336,"icon":195},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F9.cli\u002F8.worker-generators",{"title":338,"path":339,"stem":340,"icon":341},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F9.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"id":343,"title":143,"body":344,"description":2365,"extension":2366,"links":2367,"meta":2368,"navigation":2369,"path":144,"seo":2370,"stem":145,"__hash__":2371},"docs_en\u002Fen\u002F4.database\u002F2.pgx.md",{"type":345,"value":346,"toc":2356},"minimark",[347,381,432,447,452,534,567,570,681,697,701,863,884,907,911,939,958,964,968,993,1104,1115,1737,1744,1990,2036,2040,2203,2227,2231,2234,2294,2313,2320,2324,2352],[348,349,350,354,355,361,362,368,369,372,373,376,377,380],"p",{},[351,352,353],"code",{},"dbx\u002Fpg"," is the kit's default database stack, built on ",[356,357,143],"a",{"href":358,"rel":359},"https:\u002F\u002Fgithub.com\u002Fjackc\u002Fpgx",[360],"nofollow",", the most widely used PostgreSQL driver in Go. It provides three things: pool construction from a ",[356,363,365],{"href":364},"\u002Fen\u002Fdatabase\u002Foverview#config",[351,366,367],{},"dbx.Config",", a transaction-aware query executor (",[351,370,371],{},"DB","), and a ",[351,374,375],{},"dbx.Transactor"," implementation that carries a ",[351,378,379],{},"pgx.Tx"," in the context.",[382,383,388],"pre",{"className":384,"code":385,"language":386,"meta":387,"style":387},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import (\n    \"github.com\u002Fgp-system\u002Fgpsystem\u002Fdbx\"\n    \"github.com\u002Fgp-system\u002Fgpsystem\u002Fdbx\u002Fpg\"\n)\n","go","",[351,389,390,403,416,426],{"__ignoreMap":387},[391,392,395,399],"span",{"class":393,"line":394},"line",1,[391,396,398],{"class":397},"s7zQu","import",[391,400,402],{"class":401},"sMK4o"," (\n",[391,404,406,409,413],{"class":393,"line":405},2,[391,407,408],{"class":401},"    \"",[391,410,412],{"class":411},"sBMFI","github.com\u002Fgp-system\u002Fgpsystem\u002Fdbx",[391,414,415],{"class":401},"\"\n",[391,417,419,421,424],{"class":393,"line":418},3,[391,420,408],{"class":401},[391,422,423],{"class":411},"github.com\u002Fgp-system\u002Fgpsystem\u002Fdbx\u002Fpg",[391,425,415],{"class":401},[391,427,429],{"class":393,"line":428},4,[391,430,431],{"class":401},")\n",[348,433,434,435,438,439,442,443,446],{},"There is no wrapper API: the package hands back ",[351,436,437],{},"pgxpool.Pool",", ",[351,440,441],{},"pgx.Rows"," and ",[351,444,445],{},"pgconn.CommandTag",". Whatever the pgx documentation says holds here too. The kit only centralizes pool construction, instrumentation and the transaction plumbing.",[448,449,451],"h2",{"id":450},"pool","Pool",[382,453,455],{"className":384,"code":454,"language":386,"meta":387,"style":387},"pool, err := pg.NewPool(ctx, cfg.DB)  \u002F\u002F DSN from config, connect, ping\npool := pg.MustNewPool(ctx, cfg.DB)   \u002F\u002F same, panics on error, for main()\n",[351,456,457,503],{"__ignoreMap":387},[391,458,459,462,465,468,471,474,477,481,484,487,489,492,494,496,499],{"class":393,"line":394},[391,460,450],{"class":461},"sTEyZ",[391,463,464],{"class":401},",",[391,466,467],{"class":461}," err ",[391,469,470],{"class":401},":=",[391,472,473],{"class":461}," pg",[391,475,476],{"class":401},".",[391,478,480],{"class":479},"s2Zo4","NewPool",[391,482,483],{"class":401},"(",[391,485,486],{"class":461},"ctx",[391,488,464],{"class":401},[391,490,491],{"class":461}," cfg",[391,493,476],{"class":401},[391,495,371],{"class":461},[391,497,498],{"class":401},")",[391,500,502],{"class":501},"sHwdD","  \u002F\u002F DSN from config, connect, ping\n",[391,504,505,508,510,512,514,517,519,521,523,525,527,529,531],{"class":393,"line":405},[391,506,507],{"class":461},"pool ",[391,509,470],{"class":401},[391,511,473],{"class":461},[391,513,476],{"class":401},[391,515,516],{"class":479},"MustNewPool",[391,518,483],{"class":401},[391,520,486],{"class":461},[391,522,464],{"class":401},[391,524,491],{"class":461},[391,526,476],{"class":401},[391,528,371],{"class":461},[391,530,498],{"class":401},[391,532,533],{"class":501},"   \u002F\u002F same, panics on error, for main()\n",[348,535,536,538,539,542,543,546,547,550,551,555,556,558,559,562,563,566],{},[351,537,480],{}," parses the pool config from ",[351,540,541],{},"cfg.DB.DSN()",", sets ",[351,544,545],{},"MaxConns"," (",[351,548,549],{},"DB_MAX_CONNS",", default 10), connects, and ",[552,553,554],"strong",{},"verifies the connection with a ping",", so a wrong password or an unreachable database fails at startup, not on the first request. ",[351,557,516],{}," is the same with a ",[351,560,561],{},"panic","; the generated ",[351,564,565],{},"main.go"," files call it.",[348,568,569],{},"The generated entry point ties the pool's shutdown into the lifecycle:",[382,571,573],{"className":384,"code":572,"language":386,"meta":387,"style":387},"err := fiberx.Run(ctx, cfg.Server, register,\n    fiberx.WithCloser(\"pgxpool\", func(context.Context) error {\n        pool.Close()\n        return nil\n    }))\n",[351,574,575,611,654,667,675],{"__ignoreMap":387},[391,576,577,580,582,585,587,590,592,594,596,598,600,603,605,608],{"class":393,"line":394},[391,578,579],{"class":461},"err ",[391,581,470],{"class":401},[391,583,584],{"class":461}," fiberx",[391,586,476],{"class":401},[391,588,589],{"class":479},"Run",[391,591,483],{"class":401},[391,593,486],{"class":461},[391,595,464],{"class":401},[391,597,491],{"class":461},[391,599,476],{"class":401},[391,601,602],{"class":461},"Server",[391,604,464],{"class":401},[391,606,607],{"class":461}," register",[391,609,610],{"class":401},",\n",[391,612,613,616,618,621,623,626,630,632,634,637,640,642,645,647,651],{"class":393,"line":405},[391,614,615],{"class":461},"    fiberx",[391,617,476],{"class":401},[391,619,620],{"class":479},"WithCloser",[391,622,483],{"class":401},[391,624,625],{"class":401},"\"",[391,627,629],{"class":628},"sfazB","pgxpool",[391,631,625],{"class":401},[391,633,464],{"class":401},[391,635,636],{"class":401}," func(",[391,638,639],{"class":411},"context",[391,641,476],{"class":401},[391,643,644],{"class":411},"Context",[391,646,498],{"class":401},[391,648,650],{"class":649},"spNyl"," error",[391,652,653],{"class":401}," {\n",[391,655,656,659,661,664],{"class":393,"line":418},[391,657,658],{"class":461},"        pool",[391,660,476],{"class":401},[391,662,663],{"class":479},"Close",[391,665,666],{"class":401},"()\n",[391,668,669,672],{"class":393,"line":428},[391,670,671],{"class":397},"        return",[391,673,674],{"class":401}," nil\n",[391,676,678],{"class":393,"line":677},5,[391,679,680],{"class":401},"    }))\n",[348,682,683,684,689,690,693,694,696],{},"The pool is instrumented with ",[356,685,688],{"href":686,"rel":687},"https:\u002F\u002Fgithub.com\u002Fexaring\u002Fotelpgx",[360],"otelpgx",": every query emits an OTel span carrying the statement and its timing, nested under the active request\u002Ftask span. With a no-op tracer provider (dev mode, telemetry off) this is effectively free; with telemetry on the queries show up in the ",[356,691,692],{"href":233},"trace waterfall"," and on ",[356,695,237],{"href":238}," error events.",[448,698,700],{"id":699},"dbtx-the-query-surface-repositories-depend-on","DBTX: the query surface repositories depend on",[382,702,704],{"className":384,"code":703,"language":386,"meta":387,"style":387},"type DBTX interface {\n    Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)\n    Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)\n    QueryRow(ctx context.Context, sql string, args ...any) pgx.Row\n}\n",[351,705,706,719,773,819,858],{"__ignoreMap":387},[391,707,708,711,714,717],{"class":393,"line":394},[391,709,710],{"class":401},"type",[391,712,713],{"class":411}," DBTX",[391,715,716],{"class":401}," interface",[391,718,653],{"class":401},[391,720,721,724,726,729,732,734,736,738,741,744,746,749,752,755,757,759,762,764,767,769,771],{"class":393,"line":405},[391,722,723],{"class":479},"    Exec",[391,725,483],{"class":401},[391,727,486],{"class":728},"sHdIc",[391,730,731],{"class":411}," context",[391,733,476],{"class":401},[391,735,644],{"class":411},[391,737,464],{"class":401},[391,739,740],{"class":728}," sql",[391,742,743],{"class":649}," string",[391,745,464],{"class":401},[391,747,748],{"class":728}," args",[391,750,751],{"class":401}," ...",[391,753,754],{"class":411},"any",[391,756,498],{"class":401},[391,758,546],{"class":401},[391,760,761],{"class":411},"pgconn",[391,763,476],{"class":401},[391,765,766],{"class":411},"CommandTag",[391,768,464],{"class":401},[391,770,650],{"class":649},[391,772,431],{"class":401},[391,774,775,778,780,782,784,786,788,790,792,794,796,798,800,802,804,806,808,810,813,815,817],{"class":393,"line":418},[391,776,777],{"class":479},"    Query",[391,779,483],{"class":401},[391,781,486],{"class":728},[391,783,731],{"class":411},[391,785,476],{"class":401},[391,787,644],{"class":411},[391,789,464],{"class":401},[391,791,740],{"class":728},[391,793,743],{"class":649},[391,795,464],{"class":401},[391,797,748],{"class":728},[391,799,751],{"class":401},[391,801,754],{"class":411},[391,803,498],{"class":401},[391,805,546],{"class":401},[391,807,143],{"class":411},[391,809,476],{"class":401},[391,811,812],{"class":411},"Rows",[391,814,464],{"class":401},[391,816,650],{"class":649},[391,818,431],{"class":401},[391,820,821,824,826,828,830,832,834,836,838,840,842,844,846,848,850,853,855],{"class":393,"line":428},[391,822,823],{"class":479},"    QueryRow",[391,825,483],{"class":401},[391,827,486],{"class":728},[391,829,731],{"class":411},[391,831,476],{"class":401},[391,833,644],{"class":411},[391,835,464],{"class":401},[391,837,740],{"class":728},[391,839,743],{"class":649},[391,841,464],{"class":401},[391,843,748],{"class":728},[391,845,751],{"class":401},[391,847,754],{"class":411},[391,849,498],{"class":401},[391,851,852],{"class":411}," pgx",[391,854,476],{"class":401},[391,856,857],{"class":411},"Row\n",[391,859,860],{"class":393,"line":677},[391,861,862],{"class":401},"}\n",[348,864,865,866,546,869,438,871,438,873,876,877,438,880,883],{},"Every repository depends on these three methods. The return types are ",[552,867,868],{},"deliberately raw pgx types",[351,870,445],{},[351,872,441],{},[351,874,875],{},"pgx.Row","), not kit wrappers: scanning, ",[351,878,879],{},"CollectRows",[351,881,882],{},"RowsAffected()"," work exactly as the pgx documentation describes.",[348,885,886,889,890,893,894,896,897,902,903,906],{},[351,887,888],{},"DBTX"," is also ",[552,891,892],{},"method-for-method identical"," to the ",[351,895,888],{}," interface ",[356,898,901],{"href":899,"rel":900},"https:\u002F\u002Fsqlc.dev",[360],"sqlc"," generates. The kit's generators scaffold hand-written repositories, but if a project generated its query code with sqlc, those structs accept ",[351,904,905],{},"pg.NewDB(pool)"," unchanged, and participate in the same context-carried transactions.",[448,908,910],{"id":909},"db-the-executor-that-watches-the-context","DB: the executor that watches the context",[382,912,914],{"className":384,"code":913,"language":386,"meta":387,"style":387},"db := pg.NewDB(pool) \u002F\u002F implements DBTX\n",[351,915,916],{"__ignoreMap":387},[391,917,918,921,923,925,927,930,932,934,936],{"class":393,"line":394},[391,919,920],{"class":461},"db ",[391,922,470],{"class":401},[391,924,473],{"class":461},[391,926,476],{"class":401},[391,928,929],{"class":479},"NewDB",[391,931,483],{"class":401},[391,933,450],{"class":461},[391,935,498],{"class":401},[391,937,938],{"class":501}," \u002F\u002F implements DBTX\n",[348,940,941,944,945,947,948,951,952,957],{},[351,942,943],{},"pg.DB"," is the kit's single implementation of ",[351,946,888],{},", and it has exactly one trick: ",[552,949,950],{},"every call first looks for a transaction in the context",". If ",[356,953,954],{"href":153},[351,955,956],{},"WithinTransaction"," opened one, the call runs on it; if not, on the pool. The repository never knows, and never needs to know, which case it is.",[348,959,960,961,963],{},"This is where the kit's transaction model comes together: the service calls ",[351,962,956],{},", and the repositories join automatically, with unchanged signatures.",[448,965,967],{"id":966},"a-shop-repository","A shop repository",[348,969,970,971,974,975,978,979,982,983,985,986,988,989,992],{},"The ",[356,972,973],{"href":41},"shop sample app","'s module-level product repository (",[351,976,977],{},"internal\u002Fmodules\u002Fshop\u002Frepository\u002F",", shared by every surface). The repository holds a single ",[351,980,981],{},"pg.DBTX"," field. ",[351,984,565],{}," injects ",[351,987,905],{}," through ",[351,990,991],{},"Dependencies",":",[382,994,996],{"className":384,"code":995,"language":386,"meta":387,"style":387},"package repository\n\ntype ProductRepository struct {\n    db pg.DBTX\n}\n\nfunc NewProductRepository(db pg.DBTX) *ProductRepository {\n    return &ProductRepository{db: db}\n}\n",[351,997,998,1006,1012,1024,1037,1041,1046,1076,1099],{"__ignoreMap":387},[391,999,1000,1003],{"class":393,"line":394},[391,1001,1002],{"class":401},"package",[391,1004,1005],{"class":411}," repository\n",[391,1007,1008],{"class":393,"line":405},[391,1009,1011],{"emptyLinePlaceholder":1010},true,"\n",[391,1013,1014,1016,1019,1022],{"class":393,"line":418},[391,1015,710],{"class":401},[391,1017,1018],{"class":411}," ProductRepository",[391,1020,1021],{"class":401}," struct",[391,1023,653],{"class":401},[391,1025,1026,1029,1032,1034],{"class":393,"line":428},[391,1027,1028],{"class":461},"    db ",[391,1030,1031],{"class":411},"pg",[391,1033,476],{"class":401},[391,1035,1036],{"class":411},"DBTX\n",[391,1038,1039],{"class":393,"line":677},[391,1040,862],{"class":401},[391,1042,1044],{"class":393,"line":1043},6,[391,1045,1011],{"emptyLinePlaceholder":1010},[391,1047,1049,1052,1055,1057,1060,1062,1064,1066,1068,1071,1074],{"class":393,"line":1048},7,[391,1050,1051],{"class":401},"func",[391,1053,1054],{"class":479}," NewProductRepository",[391,1056,483],{"class":401},[391,1058,1059],{"class":728},"db",[391,1061,473],{"class":411},[391,1063,476],{"class":401},[391,1065,888],{"class":411},[391,1067,498],{"class":401},[391,1069,1070],{"class":401}," *",[391,1072,1073],{"class":411},"ProductRepository",[391,1075,653],{"class":401},[391,1077,1079,1082,1085,1087,1090,1092,1094,1097],{"class":393,"line":1078},8,[391,1080,1081],{"class":397},"    return",[391,1083,1084],{"class":401}," &",[391,1086,1073],{"class":411},[391,1088,1089],{"class":401},"{",[391,1091,1059],{"class":461},[391,1093,992],{"class":401},[391,1095,1096],{"class":461}," db",[391,1098,862],{"class":401},[391,1100,1102],{"class":393,"line":1101},9,[391,1103,862],{"class":401},[348,1105,1106,1107,1110,1111,1114],{},"Listing products with ",[356,1108,1109],{"href":129},"cursor pagination"," (a keyset seek, not ",[351,1112,1113],{},"OFFSET","):",[382,1116,1118],{"className":384,"code":1117,"language":386,"meta":387,"style":387},"const productSort = \"created_at DESC, id DESC\" \u002F\u002F matches the ORDER BY below\n\nfunc (r *ProductRepository) ListProducts(\n    ctx context.Context, p paginate.CursorParams,\n) (paginate.CursorPage[Product], error) {\n    args := []any{p.FetchLimit()}\n    query := `SELECT id, name, price_cents, stock, created_at FROM products`\n    if p.HasCursor() {\n        var createdAt time.Time\n        var id int64\n        if err := p.DecodeKey(&createdAt, &id); err != nil {\n            return paginate.CursorPage[Product]{}, err\n        }\n        query += ` WHERE (created_at, id) \u003C ($2, $3)`\n        args = append(args, createdAt, id)\n    }\n    query += ` ORDER BY ` + productSort + ` LIMIT $1`\n\n    rows, err := r.db.Query(ctx, query, args...)\n    if err != nil {\n        return paginate.CursorPage[Product]{}, errs.Wrap(err, \"products: list\")\n    }\n    products, err := pgx.CollectRows(rows, pgx.RowToStructByName[Product])\n    if err != nil {\n        return paginate.CursorPage[Product]{}, errs.Wrap(err, \"products: scan\")\n    }\n    return paginate.NewCursorPage(products, p, func(last Product) []any {\n        return []any{last.CreatedAt, last.ID}\n    })\n}\n",[351,1119,1120,1142,1146,1168,1194,1223,1247,1263,1280,1296,1307,1350,1372,1378,1394,1422,1428,1458,1463,1503,1516,1557,1562,1601,1614,1652,1657,1696,1726,1732],{"__ignoreMap":387},[391,1121,1122,1125,1128,1131,1134,1137,1139],{"class":393,"line":394},[391,1123,1124],{"class":401},"const",[391,1126,1127],{"class":461}," productSort ",[391,1129,1130],{"class":401},"=",[391,1132,1133],{"class":401}," \"",[391,1135,1136],{"class":628},"created_at DESC, id DESC",[391,1138,625],{"class":401},[391,1140,1141],{"class":501}," \u002F\u002F matches the ORDER BY below\n",[391,1143,1144],{"class":393,"line":405},[391,1145,1011],{"emptyLinePlaceholder":1010},[391,1147,1148,1150,1152,1155,1158,1160,1162,1165],{"class":393,"line":418},[391,1149,1051],{"class":401},[391,1151,546],{"class":401},[391,1153,1154],{"class":728},"r ",[391,1156,1157],{"class":401},"*",[391,1159,1073],{"class":411},[391,1161,498],{"class":401},[391,1163,1164],{"class":479}," ListProducts",[391,1166,1167],{"class":401},"(\n",[391,1169,1170,1173,1175,1177,1179,1181,1184,1187,1189,1192],{"class":393,"line":428},[391,1171,1172],{"class":728},"    ctx",[391,1174,731],{"class":411},[391,1176,476],{"class":401},[391,1178,644],{"class":411},[391,1180,464],{"class":401},[391,1182,1183],{"class":728}," p",[391,1185,1186],{"class":411}," paginate",[391,1188,476],{"class":401},[391,1190,1191],{"class":411},"CursorParams",[391,1193,610],{"class":401},[391,1195,1196,1198,1200,1203,1205,1208,1211,1214,1217,1219,1221],{"class":393,"line":677},[391,1197,498],{"class":401},[391,1199,546],{"class":401},[391,1201,1202],{"class":411},"paginate",[391,1204,476],{"class":401},[391,1206,1207],{"class":411},"CursorPage",[391,1209,1210],{"class":401},"[",[391,1212,1213],{"class":411},"Product",[391,1215,1216],{"class":401},"],",[391,1218,650],{"class":649},[391,1220,498],{"class":401},[391,1222,653],{"class":401},[391,1224,1225,1228,1230,1233,1235,1237,1239,1241,1244],{"class":393,"line":1043},[391,1226,1227],{"class":461},"    args ",[391,1229,470],{"class":401},[391,1231,1232],{"class":401}," []",[391,1234,754],{"class":411},[391,1236,1089],{"class":401},[391,1238,348],{"class":461},[391,1240,476],{"class":401},[391,1242,1243],{"class":479},"FetchLimit",[391,1245,1246],{"class":401},"()}\n",[391,1248,1249,1252,1254,1257,1260],{"class":393,"line":1048},[391,1250,1251],{"class":461},"    query ",[391,1253,470],{"class":401},[391,1255,1256],{"class":401}," `",[391,1258,1259],{"class":628},"SELECT id, name, price_cents, stock, created_at FROM products",[391,1261,1262],{"class":401},"`\n",[391,1264,1265,1268,1270,1272,1275,1278],{"class":393,"line":1078},[391,1266,1267],{"class":397},"    if",[391,1269,1183],{"class":461},[391,1271,476],{"class":401},[391,1273,1274],{"class":479},"HasCursor",[391,1276,1277],{"class":401},"()",[391,1279,653],{"class":401},[391,1281,1282,1285,1288,1291,1293],{"class":393,"line":1101},[391,1283,1284],{"class":401},"        var",[391,1286,1287],{"class":461}," createdAt ",[391,1289,1290],{"class":411},"time",[391,1292,476],{"class":401},[391,1294,1295],{"class":411},"Time\n",[391,1297,1299,1301,1304],{"class":393,"line":1298},10,[391,1300,1284],{"class":401},[391,1302,1303],{"class":461}," id ",[391,1305,1306],{"class":649},"int64\n",[391,1308,1310,1313,1315,1317,1319,1321,1324,1327,1330,1332,1334,1337,1340,1342,1345,1348],{"class":393,"line":1309},11,[391,1311,1312],{"class":397},"        if",[391,1314,467],{"class":461},[391,1316,470],{"class":401},[391,1318,1183],{"class":461},[391,1320,476],{"class":401},[391,1322,1323],{"class":479},"DecodeKey",[391,1325,1326],{"class":401},"(&",[391,1328,1329],{"class":461},"createdAt",[391,1331,464],{"class":401},[391,1333,1084],{"class":401},[391,1335,1336],{"class":461},"id",[391,1338,1339],{"class":401},");",[391,1341,467],{"class":461},[391,1343,1344],{"class":401},"!=",[391,1346,1347],{"class":401}," nil",[391,1349,653],{"class":401},[391,1351,1353,1356,1358,1360,1362,1364,1366,1369],{"class":393,"line":1352},12,[391,1354,1355],{"class":397},"            return",[391,1357,1186],{"class":411},[391,1359,476],{"class":401},[391,1361,1207],{"class":411},[391,1363,1210],{"class":401},[391,1365,1213],{"class":411},[391,1367,1368],{"class":401},"]{},",[391,1370,1371],{"class":461}," err\n",[391,1373,1375],{"class":393,"line":1374},13,[391,1376,1377],{"class":401},"        }\n",[391,1379,1381,1384,1387,1389,1392],{"class":393,"line":1380},14,[391,1382,1383],{"class":461},"        query ",[391,1385,1386],{"class":401},"+=",[391,1388,1256],{"class":401},[391,1390,1391],{"class":628}," WHERE (created_at, id) \u003C ($2, $3)",[391,1393,1262],{"class":401},[391,1395,1397,1400,1402,1405,1407,1410,1412,1415,1417,1420],{"class":393,"line":1396},15,[391,1398,1399],{"class":461},"        args ",[391,1401,1130],{"class":401},[391,1403,1404],{"class":479}," append",[391,1406,483],{"class":401},[391,1408,1409],{"class":461},"args",[391,1411,464],{"class":401},[391,1413,1414],{"class":461}," createdAt",[391,1416,464],{"class":401},[391,1418,1419],{"class":461}," id",[391,1421,431],{"class":401},[391,1423,1425],{"class":393,"line":1424},16,[391,1426,1427],{"class":401},"    }\n",[391,1429,1431,1433,1435,1437,1440,1443,1446,1448,1451,1453,1456],{"class":393,"line":1430},17,[391,1432,1251],{"class":461},[391,1434,1386],{"class":401},[391,1436,1256],{"class":401},[391,1438,1439],{"class":628}," ORDER BY ",[391,1441,1442],{"class":401},"`",[391,1444,1445],{"class":401}," +",[391,1447,1127],{"class":461},[391,1449,1450],{"class":401},"+",[391,1452,1256],{"class":401},[391,1454,1455],{"class":628}," LIMIT $1",[391,1457,1262],{"class":401},[391,1459,1461],{"class":393,"line":1460},18,[391,1462,1011],{"emptyLinePlaceholder":1010},[391,1464,1466,1469,1471,1473,1475,1478,1480,1482,1484,1487,1489,1491,1493,1496,1498,1500],{"class":393,"line":1465},19,[391,1467,1468],{"class":461},"    rows",[391,1470,464],{"class":401},[391,1472,467],{"class":461},[391,1474,470],{"class":401},[391,1476,1477],{"class":461}," r",[391,1479,476],{"class":401},[391,1481,1059],{"class":461},[391,1483,476],{"class":401},[391,1485,1486],{"class":479},"Query",[391,1488,483],{"class":401},[391,1490,486],{"class":461},[391,1492,464],{"class":401},[391,1494,1495],{"class":461}," query",[391,1497,464],{"class":401},[391,1499,748],{"class":461},[391,1501,1502],{"class":401},"...)\n",[391,1504,1506,1508,1510,1512,1514],{"class":393,"line":1505},20,[391,1507,1267],{"class":397},[391,1509,467],{"class":461},[391,1511,1344],{"class":401},[391,1513,1347],{"class":401},[391,1515,653],{"class":401},[391,1517,1519,1521,1523,1525,1527,1529,1531,1533,1536,1538,1541,1543,1546,1548,1550,1553,1555],{"class":393,"line":1518},21,[391,1520,671],{"class":397},[391,1522,1186],{"class":411},[391,1524,476],{"class":401},[391,1526,1207],{"class":411},[391,1528,1210],{"class":401},[391,1530,1213],{"class":411},[391,1532,1368],{"class":401},[391,1534,1535],{"class":461}," errs",[391,1537,476],{"class":401},[391,1539,1540],{"class":479},"Wrap",[391,1542,483],{"class":401},[391,1544,1545],{"class":461},"err",[391,1547,464],{"class":401},[391,1549,1133],{"class":401},[391,1551,1552],{"class":628},"products: list",[391,1554,625],{"class":401},[391,1556,431],{"class":401},[391,1558,1560],{"class":393,"line":1559},22,[391,1561,1427],{"class":401},[391,1563,1565,1568,1570,1572,1574,1576,1578,1580,1582,1585,1587,1589,1591,1594,1596,1598],{"class":393,"line":1564},23,[391,1566,1567],{"class":461},"    products",[391,1569,464],{"class":401},[391,1571,467],{"class":461},[391,1573,470],{"class":401},[391,1575,852],{"class":461},[391,1577,476],{"class":401},[391,1579,879],{"class":479},[391,1581,483],{"class":401},[391,1583,1584],{"class":461},"rows",[391,1586,464],{"class":401},[391,1588,852],{"class":461},[391,1590,476],{"class":401},[391,1592,1593],{"class":461},"RowToStructByName",[391,1595,1210],{"class":401},[391,1597,1213],{"class":461},[391,1599,1600],{"class":401},"])\n",[391,1602,1604,1606,1608,1610,1612],{"class":393,"line":1603},24,[391,1605,1267],{"class":397},[391,1607,467],{"class":461},[391,1609,1344],{"class":401},[391,1611,1347],{"class":401},[391,1613,653],{"class":401},[391,1615,1617,1619,1621,1623,1625,1627,1629,1631,1633,1635,1637,1639,1641,1643,1645,1648,1650],{"class":393,"line":1616},25,[391,1618,671],{"class":397},[391,1620,1186],{"class":411},[391,1622,476],{"class":401},[391,1624,1207],{"class":411},[391,1626,1210],{"class":401},[391,1628,1213],{"class":411},[391,1630,1368],{"class":401},[391,1632,1535],{"class":461},[391,1634,476],{"class":401},[391,1636,1540],{"class":479},[391,1638,483],{"class":401},[391,1640,1545],{"class":461},[391,1642,464],{"class":401},[391,1644,1133],{"class":401},[391,1646,1647],{"class":628},"products: scan",[391,1649,625],{"class":401},[391,1651,431],{"class":401},[391,1653,1655],{"class":393,"line":1654},26,[391,1656,1427],{"class":401},[391,1658,1660,1662,1664,1666,1669,1671,1674,1676,1678,1680,1682,1685,1688,1690,1692,1694],{"class":393,"line":1659},27,[391,1661,1081],{"class":397},[391,1663,1186],{"class":461},[391,1665,476],{"class":401},[391,1667,1668],{"class":479},"NewCursorPage",[391,1670,483],{"class":401},[391,1672,1673],{"class":461},"products",[391,1675,464],{"class":401},[391,1677,1183],{"class":461},[391,1679,464],{"class":401},[391,1681,636],{"class":401},[391,1683,1684],{"class":728},"last",[391,1686,1687],{"class":411}," Product",[391,1689,498],{"class":401},[391,1691,1232],{"class":401},[391,1693,754],{"class":411},[391,1695,653],{"class":401},[391,1697,1699,1701,1703,1705,1707,1709,1711,1714,1716,1719,1721,1724],{"class":393,"line":1698},28,[391,1700,671],{"class":397},[391,1702,1232],{"class":401},[391,1704,754],{"class":411},[391,1706,1089],{"class":401},[391,1708,1684],{"class":461},[391,1710,476],{"class":401},[391,1712,1713],{"class":461},"CreatedAt",[391,1715,464],{"class":401},[391,1717,1718],{"class":461}," last",[391,1720,476],{"class":401},[391,1722,1723],{"class":461},"ID",[391,1725,862],{"class":401},[391,1727,1729],{"class":393,"line":1728},29,[391,1730,1731],{"class":401},"    })\n",[391,1733,1735],{"class":393,"line":1734},30,[391,1736,862],{"class":401},[348,1738,1739,1740,1743],{},"And the stock decrement the service calls from the ",[351,1741,1742],{},"PlaceOrder"," transaction:",[382,1745,1747],{"className":384,"code":1746,"language":386,"meta":387,"style":387},"func (r *ProductRepository) DecrementStock(ctx context.Context, productID int64, qty int) error {\n    tag, err := r.db.Exec(ctx,\n        `UPDATE products SET stock = stock - $2 WHERE id = $1 AND stock >= $2`,\n        productID, qty)\n    if err != nil {\n        return errs.Wrap(err, \"products: decrement stock\")\n    }\n    if tag.RowsAffected() == 0 {\n        return errs.New(\"insufficient stock\",\n            errs.Code(\"insufficient_stock\"), errs.Public(\"The product is out of stock.\"))\n    }\n    return nil\n}\n",[351,1748,1749,1798,1826,1838,1849,1861,1886,1890,1913,1935,1976,1980,1986],{"__ignoreMap":387},[391,1750,1751,1753,1755,1757,1759,1761,1763,1766,1768,1770,1772,1774,1776,1778,1781,1784,1786,1789,1792,1794,1796],{"class":393,"line":394},[391,1752,1051],{"class":401},[391,1754,546],{"class":401},[391,1756,1154],{"class":728},[391,1758,1157],{"class":401},[391,1760,1073],{"class":411},[391,1762,498],{"class":401},[391,1764,1765],{"class":479}," DecrementStock",[391,1767,483],{"class":401},[391,1769,486],{"class":728},[391,1771,731],{"class":411},[391,1773,476],{"class":401},[391,1775,644],{"class":411},[391,1777,464],{"class":401},[391,1779,1780],{"class":728}," productID",[391,1782,1783],{"class":649}," int64",[391,1785,464],{"class":401},[391,1787,1788],{"class":728}," qty",[391,1790,1791],{"class":649}," int",[391,1793,498],{"class":401},[391,1795,650],{"class":649},[391,1797,653],{"class":401},[391,1799,1800,1803,1805,1807,1809,1811,1813,1815,1817,1820,1822,1824],{"class":393,"line":405},[391,1801,1802],{"class":461},"    tag",[391,1804,464],{"class":401},[391,1806,467],{"class":461},[391,1808,470],{"class":401},[391,1810,1477],{"class":461},[391,1812,476],{"class":401},[391,1814,1059],{"class":461},[391,1816,476],{"class":401},[391,1818,1819],{"class":479},"Exec",[391,1821,483],{"class":401},[391,1823,486],{"class":461},[391,1825,610],{"class":401},[391,1827,1828,1831,1834,1836],{"class":393,"line":418},[391,1829,1830],{"class":401},"        `",[391,1832,1833],{"class":628},"UPDATE products SET stock = stock - $2 WHERE id = $1 AND stock >= $2",[391,1835,1442],{"class":401},[391,1837,610],{"class":401},[391,1839,1840,1843,1845,1847],{"class":393,"line":428},[391,1841,1842],{"class":461},"        productID",[391,1844,464],{"class":401},[391,1846,1788],{"class":461},[391,1848,431],{"class":401},[391,1850,1851,1853,1855,1857,1859],{"class":393,"line":677},[391,1852,1267],{"class":397},[391,1854,467],{"class":461},[391,1856,1344],{"class":401},[391,1858,1347],{"class":401},[391,1860,653],{"class":401},[391,1862,1863,1865,1867,1869,1871,1873,1875,1877,1879,1882,1884],{"class":393,"line":1043},[391,1864,671],{"class":397},[391,1866,1535],{"class":461},[391,1868,476],{"class":401},[391,1870,1540],{"class":479},[391,1872,483],{"class":401},[391,1874,1545],{"class":461},[391,1876,464],{"class":401},[391,1878,1133],{"class":401},[391,1880,1881],{"class":628},"products: decrement stock",[391,1883,625],{"class":401},[391,1885,431],{"class":401},[391,1887,1888],{"class":393,"line":1048},[391,1889,1427],{"class":401},[391,1891,1892,1894,1897,1899,1902,1904,1907,1911],{"class":393,"line":1078},[391,1893,1267],{"class":397},[391,1895,1896],{"class":461}," tag",[391,1898,476],{"class":401},[391,1900,1901],{"class":479},"RowsAffected",[391,1903,1277],{"class":401},[391,1905,1906],{"class":401}," ==",[391,1908,1910],{"class":1909},"sbssI"," 0",[391,1912,653],{"class":401},[391,1914,1915,1917,1919,1921,1924,1926,1928,1931,1933],{"class":393,"line":1101},[391,1916,671],{"class":397},[391,1918,1535],{"class":461},[391,1920,476],{"class":401},[391,1922,1923],{"class":479},"New",[391,1925,483],{"class":401},[391,1927,625],{"class":401},[391,1929,1930],{"class":628},"insufficient stock",[391,1932,625],{"class":401},[391,1934,610],{"class":401},[391,1936,1937,1940,1942,1945,1947,1949,1952,1954,1957,1959,1961,1964,1966,1968,1971,1973],{"class":393,"line":1298},[391,1938,1939],{"class":461},"            errs",[391,1941,476],{"class":401},[391,1943,1944],{"class":479},"Code",[391,1946,483],{"class":401},[391,1948,625],{"class":401},[391,1950,1951],{"class":628},"insufficient_stock",[391,1953,625],{"class":401},[391,1955,1956],{"class":401},"),",[391,1958,1535],{"class":461},[391,1960,476],{"class":401},[391,1962,1963],{"class":479},"Public",[391,1965,483],{"class":401},[391,1967,625],{"class":401},[391,1969,1970],{"class":628},"The product is out of stock.",[391,1972,625],{"class":401},[391,1974,1975],{"class":401},"))\n",[391,1977,1978],{"class":393,"line":1309},[391,1979,1427],{"class":401},[391,1981,1982,1984],{"class":393,"line":1352},[391,1983,1081],{"class":397},[391,1985,674],{"class":401},[391,1987,1988],{"class":393,"line":1374},[391,1989,862],{"class":401},[348,1991,1992,1993,1996,1997,2000,2001,2004,2005,2011,2012,2014,2015,2018,2019,2021,2022,2018,2025,2027,2028,2031,2032,2035],{},"Notice what is ",[552,1994,1995],{},"not"," in the code: a transaction parameter. ",[351,1998,1999],{},"ListProducts"," runs on the pool when called from the public listing endpoint; ",[351,2002,2003],{},"DecrementStock",", called ",[356,2006,2007,2008,2010],{"href":153},"inside the ",[351,2009,1742],{}," transaction",", joins the same ",[351,2013,379],{},", both with the same signature. ",[351,2016,2017],{},"pgx.CollectRows"," + ",[351,2020,1593],{}," is pgx's built-in scanning; the ",[351,2023,2024],{},"WHERE ... AND stock >= $2",[351,2026,882],{}," pair is the atomic stock check, with no ",[351,2029,2030],{},"SELECT","-then-",[351,2033,2034],{},"UPDATE"," race.",[448,2037,2039],{"id":2038},"transactor","Transactor",[382,2041,2043],{"className":384,"code":2042,"language":386,"meta":387,"style":387},"tx := pg.NewTransactor(pool) \u002F\u002F dbx.Transactor\n\nerr := tx.WithinTransaction(ctx, func(ctx context.Context) error {\n    if err := s.orders.Insert(ctx, o); err != nil {\n        return err\n    }\n    return s.products.DecrementStock(ctx, o.ProductID, o.Qty) \u002F\u002F same tx\n})\n",[351,2044,2045,2068,2072,2107,2147,2153,2157,2198],{"__ignoreMap":387},[391,2046,2047,2050,2052,2054,2056,2059,2061,2063,2065],{"class":393,"line":394},[391,2048,2049],{"class":461},"tx ",[391,2051,470],{"class":401},[391,2053,473],{"class":461},[391,2055,476],{"class":401},[391,2057,2058],{"class":479},"NewTransactor",[391,2060,483],{"class":401},[391,2062,450],{"class":461},[391,2064,498],{"class":401},[391,2066,2067],{"class":501}," \u002F\u002F dbx.Transactor\n",[391,2069,2070],{"class":393,"line":405},[391,2071,1011],{"emptyLinePlaceholder":1010},[391,2073,2074,2076,2078,2081,2083,2085,2087,2089,2091,2093,2095,2097,2099,2101,2103,2105],{"class":393,"line":418},[391,2075,579],{"class":461},[391,2077,470],{"class":401},[391,2079,2080],{"class":461}," tx",[391,2082,476],{"class":401},[391,2084,956],{"class":479},[391,2086,483],{"class":401},[391,2088,486],{"class":461},[391,2090,464],{"class":401},[391,2092,636],{"class":401},[391,2094,486],{"class":728},[391,2096,731],{"class":411},[391,2098,476],{"class":401},[391,2100,644],{"class":411},[391,2102,498],{"class":401},[391,2104,650],{"class":649},[391,2106,653],{"class":401},[391,2108,2109,2111,2113,2115,2118,2120,2123,2125,2128,2130,2132,2134,2137,2139,2141,2143,2145],{"class":393,"line":428},[391,2110,1267],{"class":397},[391,2112,467],{"class":461},[391,2114,470],{"class":401},[391,2116,2117],{"class":461}," s",[391,2119,476],{"class":401},[391,2121,2122],{"class":461},"orders",[391,2124,476],{"class":401},[391,2126,2127],{"class":479},"Insert",[391,2129,483],{"class":401},[391,2131,486],{"class":461},[391,2133,464],{"class":401},[391,2135,2136],{"class":461}," o",[391,2138,1339],{"class":401},[391,2140,467],{"class":461},[391,2142,1344],{"class":401},[391,2144,1347],{"class":401},[391,2146,653],{"class":401},[391,2148,2149,2151],{"class":393,"line":677},[391,2150,671],{"class":397},[391,2152,1371],{"class":461},[391,2154,2155],{"class":393,"line":1043},[391,2156,1427],{"class":401},[391,2158,2159,2161,2163,2165,2167,2169,2171,2173,2175,2177,2179,2181,2184,2186,2188,2190,2193,2195],{"class":393,"line":1048},[391,2160,1081],{"class":397},[391,2162,2117],{"class":461},[391,2164,476],{"class":401},[391,2166,1673],{"class":461},[391,2168,476],{"class":401},[391,2170,2003],{"class":479},[391,2172,483],{"class":401},[391,2174,486],{"class":461},[391,2176,464],{"class":401},[391,2178,2136],{"class":461},[391,2180,476],{"class":401},[391,2182,2183],{"class":461},"ProductID",[391,2185,464],{"class":401},[391,2187,2136],{"class":461},[391,2189,476],{"class":401},[391,2191,2192],{"class":461},"Qty",[391,2194,498],{"class":401},[391,2196,2197],{"class":501}," \u002F\u002F same tx\n",[391,2199,2200],{"class":393,"line":1078},[391,2201,2202],{"class":401},"})\n",[348,2204,2205,2207,2208,2210,2211,2214,2215,2217,2218,2221,2222,2224,2225,476],{},[351,2206,956],{}," opens a transaction on the pool, injects the ",[351,2209,379],{}," into the context, and commits after ",[351,2212,2213],{},"fn"," returns. Any error, or panic, which is re-raised after rollback, rolls everything back. When the context already carries a transaction, ",[351,2216,2213],{}," ",[552,2219,2220],{},"joins it"," instead of opening a new one: nesting is flat, there are no savepoints, and the outermost call always owns commit\u002Frollback. The full treatment of the pattern, with the shop's ",[351,2223,1742],{}," example, is on ",[356,2226,152],{"href":153},[448,2228,2230],{"id":2229},"escape-hatch-the-context-plumbing","Escape hatch: the context plumbing",[348,2232,2233],{},"For a custom executor (say, a batch writer or a hand-instrumented query path), the transaction carrying is exported:",[382,2235,2237],{"className":384,"code":2236,"language":386,"meta":387,"style":387},"ctx = pg.ContextWithTx(ctx, tx)   \u002F\u002F put a transaction into the context\ntx, ok := pg.TxFromContext(ctx)   \u002F\u002F take the transaction out of the context\n",[351,2238,2239,2266],{"__ignoreMap":387},[391,2240,2241,2244,2246,2248,2250,2253,2255,2257,2259,2261,2263],{"class":393,"line":394},[391,2242,2243],{"class":461},"ctx ",[391,2245,1130],{"class":401},[391,2247,473],{"class":461},[391,2249,476],{"class":401},[391,2251,2252],{"class":479},"ContextWithTx",[391,2254,483],{"class":401},[391,2256,486],{"class":461},[391,2258,464],{"class":401},[391,2260,2080],{"class":461},[391,2262,498],{"class":401},[391,2264,2265],{"class":501},"   \u002F\u002F put a transaction into the context\n",[391,2267,2268,2271,2273,2276,2278,2280,2282,2285,2287,2289,2291],{"class":393,"line":405},[391,2269,2270],{"class":461},"tx",[391,2272,464],{"class":401},[391,2274,2275],{"class":461}," ok ",[391,2277,470],{"class":401},[391,2279,473],{"class":461},[391,2281,476],{"class":401},[391,2283,2284],{"class":479},"TxFromContext",[391,2286,483],{"class":401},[391,2288,486],{"class":461},[391,2290,498],{"class":401},[391,2292,2293],{"class":501},"   \u002F\u002F take the transaction out of the context\n",[348,2295,2296,2297,2299,2300,2302,2303,2305,2306,438,2309,2312],{},"Application code normally never calls these: ",[351,2298,943],{}," and the ",[351,2301,2039],{}," cover it together. They matter when you need a richer pgx surface than ",[351,2304,888],{}," (e.g. ",[351,2307,2308],{},"CopyFrom",[351,2310,2311],{},"SendBatch",") inside a transaction.",[2314,2315,2316,2317,476],"tip",{},"The transaction semantics (commit\u002Frollback\u002Fnesting\u002Fpanic) are pinned down by the kit's integration tests against a real PostgreSQL: ",[351,2318,2319],{},"go test -tags=integration .\u002Fdbx\u002Fpg\u002F",[448,2321,2323],{"id":2322},"patterns-used","Patterns used",[2325,2326,2327,2339],"ul",{},[2328,2329,2330,546,2333,2335,2336,476],"li",{},[552,2331,2332],{},"Narrow, sqlc-compatible repository interface",[351,2334,888],{},"): ",[356,2337,92],{"href":2338},"\u002Fen\u002Fconcepts\u002Fdesign-patterns#narrow-repository-interface-sqlc-compatible",[2328,2340,2341,546,2344,438,2346,2335,2349,476],{},[552,2342,2343],{},"Unit of Work \u002F transaction in context",[351,2345,956],{},[351,2347,2348],{},"txKey{}",[356,2350,92],{"href":2351},"\u002Fen\u002Fconcepts\u002Fdesign-patterns#unit-of-work--transaction-in-context",[2353,2354,2355],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":387,"searchDepth":405,"depth":405,"links":2357},[2358,2359,2360,2361,2362,2363,2364],{"id":450,"depth":405,"text":451},{"id":699,"depth":405,"text":700},{"id":909,"depth":405,"text":910},{"id":966,"depth":405,"text":967},{"id":2038,"depth":405,"text":2039},{"id":2229,"depth":405,"text":2230},{"id":2322,"depth":405,"text":2323},"The pgx-native database stack: the pool, the DBTX query surface and the context-aware DB executor.","md",null,{},{"icon":146},{"title":143,"description":2365},"uv9OJfrRzdVA-Y76jEjSomwAVlLAw2u0BNTeMyrJzOU",[2373,2375],{"title":139,"path":140,"stem":141,"description":2374,"icon":134,"children":-1},"The dbx package, the database-agnostic contract your code depends on, and the two implementations you choose by import.",{"title":148,"path":149,"stem":150,"description":2376,"icon":71,"children":-1},"The optional bun adapter: query builder and struct models, with the Transactor contract unchanged.",1784668709383]