{
  "id": "fn-sqlite-query",
  "dataComponent": "sqlite",
  "heading": {
    "title": "query",
    "badges": ["SQLite", "Streaming"]
  },
  "synopsis": "Executes a SQL query and returns an InkIterator streaming each result row as a keyed array.",
  "codeBlocks": [
    "extend(\"sqlite\")\n\n# Query rows as a streaming iterator:\nres = sqlite:open([ path: \"test.db\" ])\nhandle = res[\"handle\"]\n\niter = sqlite:query([ handle: handle, sql: \"SELECT id, name FROM users\" ])\n\narr = array:map(x => x, iter)\nsput(arr)\n# => [ [ id:1, name:\"Alice\" ], [ id:2, name:\"Bob\" ], ... ]\n\n# With parameters (for ? placeholders):\niter2 = sqlite:query([\n  handle: handle,\n  sql: \"SELECT * FROM users WHERE age > ?\",\n  params: [18, \"Alice\", 3.14]\n])\n# params array can mix int, float, and string values."
  ],
  "notes": [
    "Takes a keyed array: [ handle:int, sql:string, params?:array ].",
    "Returns an InkIterator streaming each row as a keyed array (e.g., [ col1:..., col2:... ]).",
    "The 'params' field is optional. If omitted, the SQL statement is executed with no parameters.",
    "If 'params' is provided, it can be an array containing any mix of int, float, or string values. Each element fills a SQL '?' placeholder in order.",
    "Each call to query creates a separate, isolated iterator (no locking between them).",
    "InkIterator integrates with array:map, array:reduce, flow:to_array, etc.",
    "If the handle is invalid or closed, an error is raised."
  ]
}
