executeSelectQuery runs a result-returning query against DuckDB and streams results to the client. Sends RowDescription, DataRow messages, CommandComplete, and ReadyForQuery. Returns the number of rows sent, any SQLSTATE+message sent to the client, and any connection-level error.
(query string, cmdType string)
| 151 | rewritten += ";" |
| 152 | } |
| 153 | return rewritten |
| 154 | } |
| 155 | |
| 156 | // namesDuckLakeCatalog reports whether name is this session's logical alias for |
| 157 | // the physical DuckLake catalog. True only when the session actually executes |
| 158 | // against DuckLake and its PG-visible database name differs from the physical |
| 159 | // one — i.e. the control plane accepted an org catalog name at connect. Every |
| 160 | // other session (standalone, plain "ducklake", memory) has no alias, so this is |
| 161 | // false and nothing new is rewritten. |
| 162 | // |
| 163 | // The empty-database guard is load-bearing, not belt-and-braces. `USE ""` |
| 164 | // arrives here as an empty name — rewriteDirectQuery's empty-target check runs |
| 165 | // BEFORE quote-stripping, so `""` passes it and unquotes to "". Without the |
| 166 | // guard, a session whose database is unset matches its own empty name and |
| 167 | // invalid SQL is silently rewritten into `USE ducklake.main`. |
| 168 | // |
| 169 | // TODO: a catalog-qualified `SET search_path = '<alias>.main'` is NOT rewritten |
| 170 | // and fails on the worker, while the physical `'ducklake.main'` works. Neither |
| 171 | // this function nor the transpiler's LogicalCatalogTransform sees it: the |
| 172 | // catalog name sits inside a string literal, not a RangeVar, so the AST pass |
| 173 | // has nothing to match on and this pass only inspects `USE`. Left alone |
| 174 | // deliberately — SQLMesh selects a catalog with `USE <catalog>` as its own |
| 175 | // statement (the path above), and uses search_path only in dbt code it marks |
| 176 | // unsupported. Fix it here if a client ever needs the qualified form. |
| 177 | func (c *clientConn) namesDuckLakeCatalog(name string) bool { |
| 178 | return c.physicalCatalog == physicalDuckLakeCatalog && |
| 179 | c.database != "" && |
| 180 | !strings.EqualFold(c.database, physicalDuckLakeCatalog) && |
| 181 | strings.EqualFold(c.database, name) |
| 182 | } |
| 183 | |
| 184 | // physicalDuckLakeCatalog is the physical catalog name DuckLake is attached as. |
| 185 | const physicalDuckLakeCatalog = "ducklake" |
| 186 | |
| 187 | // executeSelectQuery runs a result-returning query against DuckDB and streams results to the client. |
| 188 | // Sends RowDescription, DataRow messages, CommandComplete, and ReadyForQuery. |
| 189 | // Returns the number of rows sent, any SQLSTATE+message sent to the client, |
| 190 | // and any connection-level error. |
| 191 | func (c *clientConn) executeSelectQuery(query string, cmdType string, workerStatements ...workerStatement) (int64, string, string, error) { |
| 192 | ctx, cleanup := c.queryContext() |
| 193 | defer cleanup() |
| 194 | statement := workerStatementWithQuery(workerOriginClient, workerOperationSelect, query) |
| 195 | if len(workerStatements) > 0 { |
| 196 | statement = workerStatements[0] |
| 197 | } |
| 198 | |
| 199 | execStart := time.Now() |
| 200 | execCtx, execSpan := observe.Tracer().Start(ctx, "duckgres.execute") |
| 201 | var queryRowsAff int64 |
| 202 | var queryFinalErr error |
| 203 | c.logWorkerStatementStarted(statement) |
| 204 | defer func() { |
| 205 | c.logWorkerStatementFinished(statement, execStart, queryRowsAff, queryFinalErr) |
| 206 | }() |
| 207 | runQuery := func() (RowSet, error) { |
| 208 | return c.executor.QueryContext(ctx, query) |
| 209 | } |
| 210 |