@query-doctor/core 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/sql/analyzer.ts","../src/sql/walker.ts","../src/sql/nudges.ts","../src/sql/database.ts","../src/sql/indexes.ts","../src/sql/builder.ts","../src/sql/pg-identifier.ts","../src/optimizer/genalgo.ts","../src/optimizer/statistics.ts","../src/optimizer/pss-rewriter.ts"],"sourcesContent":["export * from \"./sql/analyzer.js\";\nexport * from \"./sql/database.js\";\nexport * from \"./sql/nudges.js\";\nexport * from \"./sql/indexes.js\";\nexport * from \"./sql/builder.js\";\nexport * from \"./sql/pg-identifier.js\";\nexport * from \"./optimizer/genalgo.js\";\nexport * from \"./optimizer/statistics.js\";\nexport * from \"./optimizer/pss-rewriter.js\";\n","import type {\n NullTestType,\n ParseResult,\n SortByDir,\n SortByNulls,\n} from \"@pgsql/types\";\nimport {\n bgMagentaBright,\n blue,\n type Color,\n dim,\n strikethrough,\n} from \"colorette\";\nimport type { RootIndexCandidate } from \"../optimizer/genalgo.js\";\nimport type { ExportedStats } from \"../optimizer/statistics.js\";\nimport type { Nudge } from \"./nudges.js\";\nimport { ColumnReferencePart, TableMappings, Walker } from \"./walker.js\";\n\nexport interface DatabaseDriver {\n query(query: string, params: unknown[]): Promise<unknown[]>;\n}\n\nexport const ignoredIdentifier = \"__qd_placeholder\";\n\nexport interface SQLCommenterTag {\n key: string;\n value: string;\n}\n\nexport type SortContext = {\n dir: SortByDir;\n nulls: SortByNulls;\n};\n\nexport type DiscoveredColumnReference = {\n /** How often the column reference appears in the query. */\n frequency: number;\n /**\n * Representation of the column reference exactly\n * as it appears in the query.\n */\n representation: string;\n /**\n * Parts of the column reference separated by dots in the query.\n * The table reference (if it exists) is resolved if the query\n * uses an alias.\n *\n * Has 3 different potential configurations (in theory)\n * `a.b.c` - a column reference with a table and a schema reference\n * `a.b` - a column reference with a table reference but no schema\n * `a` - a column reference with no table reference.\n *\n * We use a simple array here to allow parsing of any syntactically correct\n * but logically incorrect query. The checks happen later when we're deriving\n * potential indexes from parts of a column reference in `deriveIndexes`\n */\n parts: ColumnReferencePart[];\n /**\n * Whether the column reference is invalid. This\n */\n ignored: boolean;\n /** The position of the column reference in the query. */\n position: {\n start: number;\n end: number;\n };\n /**\n * A sort direction associated by the column reference.\n * Only relevant to references from sorts\n */\n sort?: SortContext;\n where?: { nulltest?: NullTestType };\n};\n\n/** A function defined by @pgsql/parser */\nexport type Parser = (query: string) => Promise<unknown>;\n\nexport type TableReference = {\n schema?: string;\n table: string;\n};\n\nexport type AnalysisResult = {\n indexesToCheck: DiscoveredColumnReference[];\n ansiHighlightedQuery: string;\n referencedTables: TableReference[];\n shadowedAliases: ColumnReferencePart[];\n tags: SQLCommenterTag[];\n queryWithoutTags: string;\n formattedQueryWithoutTags?: string;\n nudges: Nudge[];\n};\n\nexport type SQLCommenterExtraction = {\n tags: SQLCommenterTag[];\n queryWithoutTags: string;\n};\n\n/**\n * Analyzes a query and returns a list of column references that\n * should be indexed.\n *\n * This should be instantiated once per analyzed query.\n */\nexport class Analyzer {\n constructor(private readonly parser: Parser) {}\n async analyze(\n query: string,\n formattedQuery?: string,\n ): Promise<AnalysisResult> {\n const ast = (await this.parser(query)) as ParseResult;\n if (!ast.stmts) {\n throw new Error(\n \"Query did not have any statements. This should probably never happen?\",\n );\n }\n const stmt = ast.stmts[0].stmt;\n if (!stmt) {\n throw new Error(\n \"Query did not have any statements. This should probably never happen?\",\n );\n }\n const walker = new Walker(query);\n const {\n highlights,\n indexRepresentations,\n indexesToCheck,\n shadowedAliases,\n tempTables,\n tableMappings,\n nudges,\n } = walker.walk(stmt);\n const sortedHighlights = highlights.sort(\n (a, b) => b.position.end - a.position.end,\n );\n let currQuery = query;\n for (const highlight of sortedHighlights) {\n // our parts might have\n const parts = this.resolveTableAliases(highlight.parts, tableMappings);\n if (parts.length === 0) {\n console.error(highlight);\n throw new Error(\"Highlight must have at least one part\");\n }\n let color: Color;\n let skip = false;\n if (highlight.ignored) {\n color = (x) => dim(strikethrough(x));\n skip = true;\n } else if (\n parts.length === 2 &&\n tempTables.has(parts[0].text) &&\n // sometimes temp tables are aliased as existing tables\n // we don't want to ignore them if they are\n !tableMappings.has(parts[0].text)\n ) {\n color = blue;\n skip = true;\n } else {\n color = bgMagentaBright;\n }\n const queryRepr = highlight.representation;\n const queryBeforeMatch = currQuery.slice(0, highlight.position.start);\n const queryAfterToken = currQuery.slice(highlight.position.end);\n currQuery = `${queryBeforeMatch}${color(queryRepr)}${this.colorizeKeywords(\n queryAfterToken,\n color,\n )}`;\n if (indexRepresentations.has(queryRepr)) {\n skip = true;\n }\n if (!skip) {\n indexesToCheck.push(highlight);\n indexRepresentations.add(queryRepr);\n }\n }\n\n const referencedTables: TableReference[] = [];\n for (const value of tableMappings.values()) {\n // aliased mappings are not concrete tables\n // eg: select * from table t -> t is not a table\n if (!value.alias) {\n referencedTables.push({\n schema: value.schema,\n table: value.text,\n });\n }\n }\n const { tags, queryWithoutTags } = this.extractSqlcommenter(query);\n\n const formattedQueryWithoutTags = formattedQuery\n ? this.extractSqlcommenter(formattedQuery).queryWithoutTags\n : undefined;\n\n return {\n indexesToCheck,\n ansiHighlightedQuery: currQuery,\n referencedTables,\n shadowedAliases,\n tags,\n queryWithoutTags,\n formattedQueryWithoutTags,\n nudges,\n };\n }\n\n deriveIndexes(\n tables: ExportedStats[],\n discovered: DiscoveredColumnReference[],\n referencedTables: TableReference[],\n ): RootIndexCandidate[] {\n /**\n * There are 3 different kinds of parts a col reference can have\n * {a} = just a column within context. Find out the table\n * {a, b} = a column reference with a table reference. There's still ambiguity here\n * with what the schema could be in case there are 2 tables with the same name in different schemas.\n * {a, b, c} = a column reference with a table reference and a schema reference.\n * This is the best case scenario.\n */\n const allIndexes: RootIndexCandidate[] = [];\n const seenIndexes = new Set<string>();\n function addIndex(index: RootIndexCandidate) {\n const key = `\"${index.schema}\":\"${index.table}\":\"${index.column}\"`;\n if (seenIndexes.has(key)) {\n return;\n }\n seenIndexes.add(key);\n allIndexes.push(index);\n }\n const matchingTables = this.filterReferences(referencedTables, tables);\n for (const colReference of discovered) {\n const partsCount = colReference.parts.length;\n const columnOnlyReference = partsCount === 1;\n const tableReference = partsCount === 2;\n const fullReference = partsCount === 3;\n if (columnOnlyReference) {\n // select c from x\n const [column] = colReference.parts;\n const referencedColumn = this.normalize(column);\n for (const table of matchingTables) {\n if (!this.hasColumn(table, referencedColumn)) {\n continue;\n }\n const index: RootIndexCandidate = {\n schema: table.schemaName,\n table: table.tableName,\n column: referencedColumn,\n };\n if (colReference.sort) {\n index.sort = colReference.sort;\n }\n if (colReference.where) {\n index.where = colReference.where;\n }\n addIndex(index);\n }\n } else if (tableReference) {\n // select b.c from x\n const [table, column] = colReference.parts;\n const referencedTable = this.normalize(table);\n const referencedColumn = this.normalize(column);\n for (const matchingTable of matchingTables) {\n if (!this.hasColumn(matchingTable, referencedColumn)) {\n continue;\n }\n const index: RootIndexCandidate = {\n schema: matchingTable.schemaName,\n table: referencedTable,\n column: referencedColumn,\n };\n if (colReference.sort) {\n index.sort = colReference.sort;\n }\n if (colReference.where) {\n index.where = colReference.where;\n }\n addIndex(index);\n }\n } else if (fullReference) {\n // select a.b.c from x\n const [schema, table, column] = colReference.parts;\n const referencedSchema = this.normalize(schema);\n const referencedTable = this.normalize(table);\n const referencedColumn = this.normalize(column);\n const index: RootIndexCandidate = {\n schema: referencedSchema,\n table: referencedTable,\n column: referencedColumn,\n };\n if (colReference.sort) {\n index.sort = colReference.sort;\n }\n if (colReference.where) {\n index.where = colReference.where;\n }\n addIndex(index);\n } else {\n // select huh.a.b.c from x\n console.error(\n \"Column reference has too many parts. The query is malformed\",\n colReference,\n );\n continue;\n }\n }\n return allIndexes;\n }\n\n private filterReferences(\n referencedTables: TableReference[],\n tables: ExportedStats[],\n ): ExportedStats[] {\n const matchingTables: ExportedStats[] = [];\n for (const referencedTable of referencedTables) {\n const refs = tables.filter(({ tableName, schemaName }) => {\n // not every referenced table carries a schema with it\n let schemaMatches = true;\n if (referencedTable.schema) {\n schemaMatches = schemaName === referencedTable.schema;\n }\n return schemaMatches && tableName === referencedTable.table;\n });\n matchingTables.push(...refs);\n }\n return matchingTables;\n }\n\n private hasColumn(table: ExportedStats, columnName: string): boolean {\n return (\n table.columns?.some((column) => column.columnName === columnName) ?? false\n );\n }\n\n private colorizeKeywords(query: string, color: Color) {\n return query\n .replace(\n // eh? This kinda sucks\n /(^\\s+)(asc|desc)?(\\s+(nulls first|nulls last))?/i,\n (_, pre, dir, spaceNulls, nulls) => {\n return `${pre}${dir ? color(dir) : \"\"}${\n nulls ? spaceNulls.replace(nulls, color(nulls)) : \"\"\n }`;\n },\n )\n .replace(/(^\\s+)(is (null|not null))/i, (_, pre, nulltest) => {\n return `${pre}${color(nulltest)}`;\n });\n }\n\n /**\n * Resolves aliases such as `a.b` to `x.b` if `a` is a known\n * alias to a table called x.\n *\n * Ignores all other combination of parts such as `a.b.c`\n */\n private resolveTableAliases(\n parts: ColumnReferencePart[],\n tableMappings: TableMappings,\n ): ColumnReferencePart[] {\n // we don't want to resolve aliases for references such as\n // `a.b.c` - this is fully qualified with a schema and can't be an alias\n // `c` - because there's no table reference here (as far as we can tell)\n if (parts.length !== 2) {\n return parts;\n }\n const tablePart = parts[0];\n const mapping = tableMappings.get(tablePart.text);\n if (mapping) {\n parts[0] = mapping;\n }\n return parts;\n }\n\n private normalize(columnReference: ColumnReferencePart): string {\n return columnReference.quoted\n ? columnReference.text\n : // postgres automatically lowercases column names if not quoted\n columnReference.text.toLowerCase();\n }\n\n private extractSqlcommenter(query: string): SQLCommenterExtraction {\n const trimmedQuery = query.trimEnd();\n const startPosition = trimmedQuery.lastIndexOf(\"/*\");\n const endPosition = trimmedQuery.lastIndexOf(\"*/\");\n if (startPosition === -1 || endPosition === -1) {\n return { tags: [], queryWithoutTags: trimmedQuery };\n }\n const queryWithoutTags = trimmedQuery.slice(0, startPosition);\n const tagString = trimmedQuery.slice(startPosition + 2, endPosition).trim();\n if (!tagString || typeof tagString !== \"string\") {\n return { tags: [], queryWithoutTags: queryWithoutTags };\n }\n const tags: SQLCommenterTag[] = [];\n for (const match of tagString.split(\",\")) {\n const [key, value] = match.split(\"=\");\n // just because a comment has a `,` but not a `=` in it doesn't\n // mean that it's a malformed sqlcommenter tag. It might just be\n // a long comment with good punctuation.\n if (!key || !value) {\n // however, if there was a previously valid tag, the comment\n // is more likely to be a malformed sqlcommenter tag\n if (tags.length > 0) {\n console.warn(\n `Invalid sqlcommenter tag: ${match} in comment: ${tagString}. Ignoring`,\n );\n }\n continue;\n }\n try {\n let sliceStart = 0;\n if (value.startsWith(\"'\")) {\n sliceStart = 1;\n }\n let sliceEnd = value.length;\n if (value.endsWith(\"'\")) {\n sliceEnd -= 1;\n }\n\n const decoded = decodeURIComponent(value.slice(sliceStart, sliceEnd));\n // should we be trimming here?\n tags.push({ key: key.trim(), value: decoded });\n } catch (err) {\n // we want to be very conservative with this parser and ignore errors\n console.error(err);\n }\n }\n return { tags, queryWithoutTags };\n }\n}\n","import type { Node, NullTestType } from \"@pgsql/types\";\nimport { deparseSync } from \"pgsql-deparser\";\nimport type { DiscoveredColumnReference, SortContext } from \"./analyzer.js\";\nimport { parseNudges } from \"./nudges.js\";\nimport type { Nudge } from \"./nudges.js\";\n\n/** Information about tables that appear in the query */\nexport type TableMappings = Map<string, ColumnReferencePart>;\ntype SeenReferences = Map<string, number>;\n\n/**\n * Walks the AST of a sql query and extracts query metadata.\n * This pattern is used to segregate the mutable state that's more common for the\n * AST walking process from the rest of the analyzer.\n */\nexport class Walker {\n private tableMappings: TableMappings = new Map();\n private tempTables = new Set<string>();\n private highlights: DiscoveredColumnReference[] = [];\n private indexRepresentations = new Set<string>();\n private indexesToCheck: DiscoveredColumnReference[] = [];\n private highlightPositions = new Set<number>();\n // used for tallying the amount of times we see stuff so\n // we have a better idea of what to start off the algorithm with\n private seenReferences: SeenReferences = new Map();\n private shadowedAliases: ColumnReferencePart[] = [];\n private nudges: Nudge[] = [];\n\n constructor(private readonly query: string) {}\n\n walk(root: Node) {\n // reset state in case the class instance is reused\n // reassigning vars here instead of using `map.clear()` to prevent\n // accidentally mutating existing references\n this.tableMappings = new Map();\n this.tempTables = new Set<string>();\n this.highlights = [];\n this.indexRepresentations = new Set<string>();\n this.indexesToCheck = [];\n this.highlightPositions = new Set<number>();\n this.seenReferences = new Map();\n this.shadowedAliases = [];\n this.nudges = [];\n\n Walker.traverse(root, [], (node, stack) => {\n const nodeNudges = parseNudges(node, stack);\n this.nudges = [...this.nudges, ...nodeNudges];\n\n // comments are not parsed here as they seem to be ignored.\n //\n // results cannot be indexed in any way because they alias a CTE\n // with alias as (select ...)\n // ^^^^^\n if (is(node, \"CommonTableExpr\")) {\n if (node.CommonTableExpr.ctename) {\n this.tempTables.add(node.CommonTableExpr.ctename);\n }\n }\n // results cannot be indexed in any way because they alias a subquery\n // select ... from (...) as alias\n // ^^^^^\n if (is(node, \"RangeSubselect\")) {\n if (node.RangeSubselect.alias?.aliasname) {\n this.tempTables.add(node.RangeSubselect.alias.aliasname);\n }\n }\n // select ... from (...) where col is null\n // ^^^^^^^\n if (is(node, \"NullTest\")) {\n if (\n node.NullTest.arg &&\n node.NullTest.nulltesttype &&\n is(node.NullTest.arg, \"ColumnRef\")\n ) {\n this.add(node.NullTest.arg, {\n where: { nulltest: node.NullTest.nulltesttype },\n });\n }\n }\n // can be indexed as the alias refers to a regular table\n // but the alias has to be mapped to the original table name\n // select ... from table as alias\n // ^^^^^\n if (is(node, \"RangeVar\") && node.RangeVar.relname) {\n const columnReference: ColumnReferencePart = {\n text: node.RangeVar.relname,\n start: node.RangeVar.location,\n quoted: false,\n };\n if (node.RangeVar.schemaname) {\n columnReference.schema = node.RangeVar.schemaname;\n }\n this.tableMappings.set(node.RangeVar.relname, columnReference);\n // In theory we can't blindly map aliases to table names\n // it's possible that two aliases point to different tables\n // which postgres allows but is tricky to determine by just walking\n // the AST like we're doing currently.\n if (node.RangeVar.alias?.aliasname) {\n const aliasName = node.RangeVar.alias.aliasname;\n const existingMapping = this.tableMappings.get(aliasName);\n const part: ColumnReferencePart = {\n text: node.RangeVar.relname,\n start: node.RangeVar.location,\n // what goes here? the text here doesn't _really_ exist.\n // so it can't be quoted or not quoted.\n // Does it even matter?\n quoted: true,\n alias: aliasName,\n };\n if (node.RangeVar.schemaname) {\n part.schema = node.RangeVar.schemaname;\n }\n // Postgres supports shadowing table aliases created in different levels of queries\n // for example:\n // ```\n // SELECT t.id, t.name\n // FROM users t\n // WHERE EXISTS (\n // SELECT 1\n // FROM orders t -- shadows outer alias \"t\"\n // WHERE t.user_id = users.id\n // );\n // ```\n // but we're very unlikely to see this in practice. Every ORM I've seen so far\n // has produced globally unique aliases. This is not worth the complexity currently.\n // but is almost certainly guaranteed to be a problem in the future.\n if (existingMapping) {\n const isSystemCatalog =\n node.RangeVar.relname?.startsWith(\"pg_\") ?? false;\n if (!isSystemCatalog) {\n console.warn(\n `Ignoring alias ${aliasName} as it shadows an existing mapping for ${existingMapping.text}. We currently do not support alias shadowing.`,\n );\n }\n // Let the user know what happened but don't stop the show.\n this.shadowedAliases.push(part);\n return;\n }\n this.tableMappings.set(aliasName, part);\n }\n }\n // select ... from table order by col asc\n // ^^^^^^^^^^^^^^^^\n if (is(node, \"SortBy\")) {\n // we don't care about sorting by anything that's not a column reference\n // because it couldn't be indexed anyway.\n // TODO: mark that expression as unindexable? It's just better for debugging\n if (node.SortBy.node && is(node.SortBy.node, \"ColumnRef\")) {\n this.add(node.SortBy.node, {\n sort: {\n dir: node.SortBy.sortby_dir ?? \"SORTBY_DEFAULT\",\n nulls: node.SortBy.sortby_nulls ?? \"SORTBY_NULLS_DEFAULT\",\n },\n });\n }\n }\n // select ... from table1 join table2 t2 on table1.col = t2.col\n // ^^\n if (is(node, \"JoinExpr\") && node.JoinExpr.quals) {\n if (is(node.JoinExpr.quals, \"A_Expr\")) {\n if (\n node.JoinExpr.quals.A_Expr.lexpr &&\n is(node.JoinExpr.quals.A_Expr.lexpr, \"ColumnRef\")\n ) {\n this.add(node.JoinExpr.quals.A_Expr.lexpr);\n }\n if (\n node.JoinExpr.quals.A_Expr.rexpr &&\n is(node.JoinExpr.quals.A_Expr.rexpr, \"ColumnRef\")\n ) {\n this.add(node.JoinExpr.quals.A_Expr.rexpr);\n }\n }\n }\n // any column reference anywhere\n if (is(node, \"ColumnRef\")) {\n // TODO: this approach needs refinement\n for (let i = 0; i < stack.length; i++) {\n const inReturningList =\n stack[i] === \"returningList\" &&\n stack[i + 1] === \"ResTarget\" &&\n stack[i + 2] === \"val\" &&\n stack[i + 3] === \"ColumnRef\";\n if (inReturningList) {\n this.add(node, { ignored: true });\n return;\n }\n if (\n // stack[i] === \"SelectStmt\" &&\n stack[i + 1] === \"targetList\" &&\n stack[i + 2] === \"ResTarget\" &&\n stack[i + 3] === \"val\" &&\n stack[i + 4] === \"ColumnRef\"\n ) {\n // we don't want to index the columns that are being selected\n this.add(node, { ignored: true });\n return;\n }\n\n // TODO: add functional index support here\n if (stack[i] === \"FuncCall\" && stack[i + 1] === \"args\") {\n // args of a function call can't be indexed (without functional indexes)\n this.add(node, { ignored: true });\n return;\n }\n }\n this.add(node);\n }\n });\n\n return {\n highlights: this.highlights,\n indexRepresentations: this.indexRepresentations,\n indexesToCheck: this.indexesToCheck,\n shadowedAliases: this.shadowedAliases,\n tempTables: this.tempTables,\n tableMappings: this.tableMappings,\n nudges: this.nudges,\n };\n }\n\n private add(\n node: Extract<Node, { ColumnRef: unknown }>,\n options?: {\n ignored?: boolean;\n sort?: SortContext;\n where?: { nulltest?: NullTestType };\n },\n ) {\n if (!node.ColumnRef.location) {\n console.error(`Node did not have a location. Skipping`, node);\n return;\n }\n if (!node.ColumnRef.fields) {\n console.error(node);\n throw new Error(\"Column reference must have fields\");\n }\n let ignored = options?.ignored ?? false;\n let runningLength: number = node.ColumnRef.location;\n const parts: ColumnReferencePart[] = node.ColumnRef.fields.map(\n (field, i, length) => {\n if (!is(field, \"String\") || !field.String.sval) {\n const out = deparseSync(field);\n ignored = true;\n return {\n quoted: out.startsWith('\"'),\n text: out,\n start: runningLength,\n };\n }\n const start = runningLength;\n const size = field.String.sval?.length ?? 0;\n let quoted = false;\n if (node.ColumnRef.location !== undefined) {\n const boundary = this.query[runningLength];\n if (boundary === '\"') {\n quoted = true;\n }\n }\n // +1 for the dot that comes after\n const isLastIteration = i === length.length - 1;\n runningLength += size + (isLastIteration ? 0 : 1) + (quoted ? 2 : 0);\n return {\n text: field.String.sval,\n start,\n quoted,\n };\n },\n );\n const end = runningLength;\n if (this.highlightPositions.has(node.ColumnRef.location)) {\n return;\n }\n this.highlightPositions.add(node.ColumnRef.location);\n const highlighted = `${this.query.slice(node.ColumnRef.location, end)}`;\n const seen = this.seenReferences.get(highlighted);\n if (!ignored) {\n this.seenReferences.set(highlighted, (seen ?? 0) + 1);\n }\n const ref: DiscoveredColumnReference = {\n frequency: seen ?? 1,\n representation: highlighted,\n parts,\n ignored: ignored ?? false,\n position: {\n start: node.ColumnRef.location,\n end,\n },\n };\n if (options?.sort) {\n ref.sort = options.sort;\n }\n if (options?.where) {\n ref.where = options.where;\n }\n this.highlights.push(ref);\n }\n\n static traverse(\n node: unknown,\n stack: (KeysOfUnion<Node> | string)[],\n callback: (node: Node, stack: (KeysOfUnion<Node> | string)[]) => void,\n ) {\n if (isANode(node)) {\n callback(node, [...stack, getNodeKind(node)]);\n }\n if (typeof node !== \"object\" || node === null) {\n return;\n }\n if (Array.isArray(node)) {\n for (const item of node) {\n if (isANode(item)) {\n Walker.traverse(item, stack, callback);\n }\n }\n } else if (isANode(node)) {\n const keys = Object.keys(node);\n // @ts-expect-error | nodes don't allow dynamic access but it's the only way to do it\n Walker.traverse(node[keys[0]], [...stack, getNodeKind(node)], callback);\n } else {\n for (const [key, child] of Object.entries(node)) {\n Walker.traverse(child, [...stack, key as KeysOfUnion<Node>], callback);\n }\n }\n }\n}\n\nexport type ColumnReferencePart = {\n schema?: string;\n /** the text of the column reference (excluding any potential quotes) */\n text: string;\n start?: number;\n quoted: boolean;\n alias?: string;\n};\n\ntype KeysOfUnion<T> = T extends T ? keyof T : never;\nfunction is<K extends KeysOfUnion<Node>>(\n node: Node,\n kind: K,\n): node is Extract<Node, Record<K, unknown>> {\n return kind in node;\n}\n\nfunction getNodeKind(node: Node): KeysOfUnion<Node> {\n const keys = Object.keys(node);\n return keys[0] as KeysOfUnion<Node>;\n}\n\nfunction isANode(node: unknown): node is Node {\n if (typeof node !== \"object\" || node === null) {\n return false;\n }\n const keys = Object.keys(node);\n return keys.length === 1 && /^[A-Z]/.test(keys[0]);\n}\n","import type { List, Node } from \"@pgsql/types\";\n\ntype NudgeKind =\n | \"LARGE_IMPROVEMENT_FOUND\"\n | \"SMALL_IMPROVEMENT_FOUND\"\n | \"AVOID_SELECT_STAR\"\n | \"AVOID_FUNCTIONS_ON_COLUMNS_IN_WHERE\"\n | \"MISSING_WHERE_CLAUSE\"\n | \"MISSING_LIMIT_CLAUSE\"\n | \"USE_IS_NULL_NOT_EQUALS\"\n | \"AVOID_DISTINCT_WITHOUT_REASON\"\n | \"MISSING_JOIN_CONDITION\"\n | \"AVOID_LEADING_WILDCARD_LIKE\"\n | \"CONSIDER_IN_INSTEAD_OF_MANY_ORS\"\n | \"REPLACE_LARGE_IN_TUPLE_WITH_ANY_ARRAY\";\n\nexport type Nudge = {\n kind: NudgeKind;\n severity: \"CRITICAL\" | \"WARNING\" | \"INFO\";\n message: string;\n};\n\ntype KeysOfUnion<T> = T extends T ? keyof T : never;\n\nfunction is<K extends KeysOfUnion<Node>>(\n node: Node,\n kind: K,\n): node is Extract<Node, Record<K, unknown>> {\n return kind in node;\n}\n\nfunction isANode(node: unknown): node is Node {\n if (typeof node !== \"object\" || node === null) {\n return false;\n }\n const keys = Object.keys(node);\n return keys.length === 1 && /^[A-Z]/.test(keys[0]);\n}\n\n/**\n * Detect nudges for a single node during AST traversal.\n * Returns an array of nudges found for this node.\n */\nexport function parseNudges(\n node: Node,\n stack: (KeysOfUnion<Node> | string)[],\n): Nudge[] {\n const nudges: Nudge[] = [];\n\n if (is(node, \"A_Star\")) {\n nudges.push({\n kind: \"AVOID_SELECT_STAR\",\n severity: \"INFO\",\n message: \"Avoid using SELECT *\",\n });\n }\n\n // Detect functions on columns in WHERE clause\n if (is(node, \"FuncCall\")) {\n // Check if we're in a WHERE clause context\n const inWhereClause = stack.some((item) => item === \"whereClause\");\n if (inWhereClause && node.FuncCall.args) {\n // Check if any function arguments contain column references\n const hasColumnRef = containsColumnRef(node.FuncCall.args);\n if (hasColumnRef) {\n nudges.push({\n kind: \"AVOID_FUNCTIONS_ON_COLUMNS_IN_WHERE\",\n severity: \"WARNING\",\n message: \"Avoid using functions on columns in WHERE clause\",\n });\n }\n }\n }\n\n // Detect unbounded queries (missing WHERE or LIMIT on table queries)\n if (is(node, \"SelectStmt\")) {\n // Only check top-level SELECT statements (not subqueries)\n const isSubquery = stack.some(\n (item) =>\n item === \"RangeSubselect\" ||\n item === \"SubLink\" ||\n item === \"CommonTableExpr\",\n );\n\n if (!isSubquery) {\n const hasFromClause =\n node.SelectStmt.fromClause && node.SelectStmt.fromClause.length > 0;\n if (hasFromClause) {\n // Check if this SELECT queries actual tables (not just subqueries or CTEs)\n const hasActualTables = node.SelectStmt.fromClause!.some((fromItem) => {\n return (\n is(fromItem, \"RangeVar\") ||\n (is(fromItem, \"JoinExpr\") && hasActualTablesInJoin(fromItem))\n );\n });\n\n if (hasActualTables) {\n // Check for missing WHERE clause\n if (!node.SelectStmt.whereClause) {\n nudges.push({\n kind: \"MISSING_WHERE_CLAUSE\",\n severity: \"INFO\",\n message: \"Missing WHERE clause\",\n });\n }\n\n // Check for missing LIMIT clause\n if (!node.SelectStmt.limitCount) {\n nudges.push({\n kind: \"MISSING_LIMIT_CLAUSE\",\n severity: \"INFO\",\n message: \"Missing LIMIT clause\",\n });\n }\n }\n }\n }\n }\n\n // Detect NULL comparison issues (= NULL instead of IS NULL)\n if (is(node, \"A_Expr\")) {\n const isEqualityOp =\n node.A_Expr.kind === \"AEXPR_OP\" &&\n node.A_Expr.name &&\n node.A_Expr.name.length > 0 &&\n is(node.A_Expr.name[0], \"String\") &&\n (node.A_Expr.name[0].String.sval === \"=\" ||\n node.A_Expr.name[0].String.sval === \"!=\" ||\n node.A_Expr.name[0].String.sval === \"<>\");\n\n if (isEqualityOp) {\n const leftIsNull = isNullConstant(node.A_Expr.lexpr);\n const rightIsNull = isNullConstant(node.A_Expr.rexpr);\n\n if (leftIsNull || rightIsNull) {\n nudges.push({\n kind: \"USE_IS_NULL_NOT_EQUALS\",\n severity: \"WARNING\",\n message: \"Use IS NULL instead of = or != or <> for NULL comparisons\",\n });\n }\n }\n\n // Detect LIKE with leading wildcards\n const isLikeOp =\n node.A_Expr.kind === \"AEXPR_LIKE\" || node.A_Expr.kind === \"AEXPR_ILIKE\";\n\n if (isLikeOp && node.A_Expr.rexpr) {\n const patternString = getStringConstantValue(node.A_Expr.rexpr);\n if (patternString && patternString.startsWith(\"%\")) {\n nudges.push({\n kind: \"AVOID_LEADING_WILDCARD_LIKE\",\n severity: \"WARNING\",\n message: \"Avoid using LIKE with leading wildcards\",\n });\n }\n }\n }\n\n // Detect DISTINCT usage\n if (is(node, \"SelectStmt\") && node.SelectStmt.distinctClause) {\n nudges.push({\n kind: \"AVOID_DISTINCT_WITHOUT_REASON\",\n severity: \"WARNING\",\n message: \"Avoid using DISTINCT without a reason\",\n });\n }\n\n // Detect cartesian joins (missing JOIN conditions)\n if (is(node, \"JoinExpr\")) {\n // Check if JOIN has no qualification (ON clause)\n if (!node.JoinExpr.quals) {\n nudges.push({\n kind: \"MISSING_JOIN_CONDITION\",\n severity: \"WARNING\",\n message: \"Missing JOIN condition\",\n });\n }\n }\n\n // Detect multiple tables in FROM without explicit JOINs (old-style cartesian joins)\n if (\n is(node, \"SelectStmt\") &&\n node.SelectStmt.fromClause &&\n node.SelectStmt.fromClause.length > 1\n ) {\n // Check if there are multiple RangeVar (tables) directly in FROM clause\n const tableCount = node.SelectStmt.fromClause.filter((item) =>\n is(item, \"RangeVar\"),\n ).length;\n if (tableCount > 1) {\n nudges.push({\n kind: \"MISSING_JOIN_CONDITION\",\n severity: \"WARNING\",\n message: \"Missing JOIN condition\",\n });\n }\n }\n\n // Detect too many OR conditions\n if (is(node, \"BoolExpr\") && node.BoolExpr.boolop === \"OR_EXPR\") {\n const orCount = countBoolOrConditions(node);\n if (orCount >= 3) {\n nudges.push({\n kind: \"CONSIDER_IN_INSTEAD_OF_MANY_ORS\",\n severity: \"WARNING\",\n message: \"Consider using IN instead of many ORs\",\n });\n }\n }\n\n // Too many parameters in a tuple\n if (is(node, \"A_Expr\")) {\n if (node.A_Expr.kind === \"AEXPR_IN\") {\n let list: List | undefined;\n if (node.A_Expr.lexpr && is(node.A_Expr.lexpr, \"List\")) {\n list = node.A_Expr.lexpr.List;\n } else if (node.A_Expr.rexpr && is(node.A_Expr.rexpr, \"List\")) {\n list = node.A_Expr.rexpr.List;\n }\n\n if (list?.items && list.items.length >= 10) {\n nudges.push({\n kind: \"REPLACE_LARGE_IN_TUPLE_WITH_ANY_ARRAY\",\n message:\n \"`in (...)` queries with large tuples can often be replaced with `= ANY($1)` using a single parameter\",\n severity: \"INFO\",\n });\n }\n }\n }\n\n return nudges;\n}\n\nfunction containsColumnRef(args: unknown[]): boolean {\n // Recursively check if any argument contains a ColumnRef\n for (const arg of args) {\n if (hasColumnRefInNode(arg)) {\n return true;\n }\n }\n return false;\n}\n\nfunction hasColumnRefInNode(node: unknown): boolean {\n if (isANode(node) && is(node, \"ColumnRef\")) {\n return true;\n }\n\n if (typeof node !== \"object\" || node === null) {\n return false;\n }\n\n if (Array.isArray(node)) {\n return node.some((item) => hasColumnRefInNode(item));\n }\n\n if (isANode(node)) {\n const keys = Object.keys(node);\n // @ts-expect-error | nodes don't allow dynamic access but it's the only way to do it\n return hasColumnRefInNode(node[keys[0]]);\n }\n\n for (const child of Object.values(node)) {\n if (hasColumnRefInNode(child)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction hasActualTablesInJoin(\n joinExpr: Extract<Node, { JoinExpr: unknown }>,\n): boolean {\n // Check left side of join\n if (joinExpr.JoinExpr.larg && is(joinExpr.JoinExpr.larg, \"RangeVar\")) {\n return true;\n }\n if (joinExpr.JoinExpr.larg && is(joinExpr.JoinExpr.larg, \"JoinExpr\")) {\n if (hasActualTablesInJoin(joinExpr.JoinExpr.larg)) {\n return true;\n }\n }\n\n // Check right side of join\n if (joinExpr.JoinExpr.rarg && is(joinExpr.JoinExpr.rarg, \"RangeVar\")) {\n return true;\n }\n if (joinExpr.JoinExpr.rarg && is(joinExpr.JoinExpr.rarg, \"JoinExpr\")) {\n if (hasActualTablesInJoin(joinExpr.JoinExpr.rarg)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction isNullConstant(node: unknown): boolean {\n if (!node || typeof node !== \"object\") {\n return false;\n }\n\n if (isANode(node) && is(node, \"A_Const\")) {\n // Check if it's a NULL constant\n return node.A_Const.isnull !== undefined;\n }\n\n return false;\n}\n\nfunction getStringConstantValue(node: unknown): string | null {\n if (!node || typeof node !== \"object\") {\n return null;\n }\n\n if (isANode(node) && is(node, \"A_Const\") && node.A_Const.sval) {\n return node.A_Const.sval.sval || null;\n }\n\n return null;\n}\n\nfunction countBoolOrConditions(\n node: Extract<Node, { BoolExpr: unknown }>,\n): number {\n if (node.BoolExpr.boolop !== \"OR_EXPR\" || !node.BoolExpr.args) {\n return 1;\n }\n\n let count = 0;\n for (const arg of node.BoolExpr.args) {\n if (\n isANode(arg) &&\n is(arg, \"BoolExpr\") &&\n arg.BoolExpr.boolop === \"OR_EXPR\"\n ) {\n count += countBoolOrConditions(arg);\n } else {\n count += 1;\n }\n }\n\n return count;\n}\n","import { z } from \"zod\";\nimport type { PgIdentifier } from \"./pg-identifier\";\n\nexport const PostgresVersion = z.string().brand(\"PostgresVersion\");\nexport type PostgresVersion = z.infer<typeof PostgresVersion>;\n\nexport interface PostgresTransaction {\n /**\n * Exec a query and return the result as an array of objects.\n */\n exec<T>(query: string, params?: unknown[]): Promise<T[]>;\n}\n\n/**\n * A shared interface for all postgres connections.\n * This is required to allow interop between pglite and regular postgres drivers\n */\nexport interface Postgres extends PostgresTransaction {\n transaction<T>(callback: (tx: PostgresTransaction) => Promise<T>): Promise<T>;\n cursor?<T>(\n query: string,\n params?: unknown[],\n options?: { size?: number },\n ): AsyncGenerator<T, void, unknown>;\n // postgres returns versions as a string\n serverNum(): Promise<PostgresVersion>;\n}\n\nexport type PostgresConnectionInput = {\n url: string;\n};\n\nexport type PostgresFactory = (input: PostgresConnectionInput) => Postgres;\n\n// PostgreSQL EXPLAIN plan types\ndeclare const StageId: unique symbol;\nexport type PostgresStageId = number & { [StageId]: \"StageId\" };\n\nexport type PostgresStage =\n | \"Seq Scan\"\n | \"Limit\"\n | \"Bitmap Heap Scan\"\n | \"Bitmap Index Scan\"\n | \"Index Only Scan\"\n | \"Index Scan\"\n | \"BitmapOr\";\n\nexport type PostgresExplainStageCommon = {\n \"Node Type\": PostgresStage;\n // available only when doing a trace explain using the\n // query doctor postgres binary\n \"Node Id\": PostgresStageId;\n Plans?: PostgresExplainStage[];\n \"Plan Width\": number;\n \"Total Cost\": number;\n};\n\nexport type PostgresExplainStage =\n | (PostgresExplainStageCommon & {\n \"Node Type\": \"Index Scan\";\n \"Index Name\": string;\n \"Relation Name\": string;\n Alias: string;\n \"Rows Removed by Filter\": number;\n })\n | (PostgresExplainStageCommon & {\n \"Node Type\": \"Seq Scan\";\n \"Relation Name\": string;\n Filter?: string;\n \"Actual Rows\": number;\n \"Actual Loops\": number;\n \"Rows Removed by Filter\": number;\n })\n | (PostgresExplainStageCommon & {\n \"Node Type\": \"Bitmap Heap Scan\";\n \"Relation Name\": string;\n Filter?: string;\n \"Actual Rows\": number;\n \"Actual Loops\": number;\n \"Rows Removed by Filter\": number;\n })\n | (PostgresExplainStageCommon & {\n \"Node Type\": \"Bitmap Index Scan\";\n \"Index Name\": string;\n Filter?: string;\n \"Actual Rows\": number;\n \"Actual Loops\": number;\n \"Rows Removed by Filter\": number;\n })\n | (PostgresExplainStageCommon & {\n \"Node Type\": \"Index Only Scan\";\n \"Index Name\": string;\n Filter?: string;\n \"Actual Rows\": number;\n \"Actual Loops\": number;\n \"Rows Removed by Filter\": number;\n })\n | PostgresExplainStageCommon;\n\nexport type PostgresExplainResult = {\n \"QUERY PLAN\": {\n Plan: PostgresExplainStage;\n }[];\n};\n\n/**\n * Drops a disabled index. Rollsback if it fails for any reason\n * @returns Did dropping the index succeed?\n */\nexport async function dropIndex(\n tx: PostgresTransaction,\n index: PgIdentifier,\n): Promise<boolean> {\n try {\n await tx.exec(`\n savepoint idx_drop;\n drop index if exists ${index} cascade;\n `);\n return true;\n } catch (error) {\n // no problem if droping the index fails. It should throw an error\n await tx.exec(`rollback to idx_drop`);\n return false;\n }\n}\n","import type { IndexedTable } from \"../optimizer/statistics.js\";\n\nexport function isIndexSupported(index: IndexedTable) {\n return index.index_type === \"btree\";\n}\n\n/**\n * Doesn't necessarily decide whether the index can be dropped but can be\n * used to not even try dropping indexes that _definitely_ cannot be dropped\n */\nexport function isIndexProbablyDroppable(index: IndexedTable) {\n /* TODO: until we have a better solution, this is the best we have */\n /* The is_unique check is problematic only if the column is declared as unique */\n return !index.is_primary && !index.is_unique;\n}\n","import { PgIdentifier } from \"./pg-identifier\";\n\nexport type PostgresQueryBuilderCommand =\n | \"bitmapscan\"\n | \"indexscan\"\n | \"seqscan\";\n\nexport class PostgresQueryBuilder {\n private readonly commands: Record<string, string> = {};\n private isIntrospection: boolean = false;\n private explainFlags: string[] = [];\n private _preamble = 0;\n\n constructor(private query: string) {}\n\n get preamble(): number {\n return this._preamble;\n }\n\n static createIndex(definition: string, name?: PgIdentifier) {\n if (name) {\n return new PostgresQueryBuilder(\n `create index \"${name}\" on ${definition};`,\n );\n }\n return new PostgresQueryBuilder(`create index on ${definition};`);\n }\n\n enable(command: PostgresQueryBuilderCommand, value: boolean = true): this {\n const commandString = `enable_${command}`;\n if (value) {\n this.commands[commandString] = \"on\";\n } else {\n this.commands[commandString] = \"off\";\n }\n return this;\n }\n\n withQuery(query: string): this {\n this.query = query;\n return this;\n }\n\n introspect(): this {\n this.isIntrospection = true;\n return this;\n }\n\n explain(flags: string[]): this {\n this.explainFlags = flags;\n return this;\n }\n\n build(): string {\n let commands = this.generateSetCommands();\n commands += this.generateExplain().query;\n if (this.isIntrospection) {\n commands += \" -- @qd_introspection\";\n }\n return commands;\n }\n\n /** Return the \"set a=b\" parts of the command in the query separate from the explain select ... part */\n buildParts() {\n const commands = this.generateSetCommands();\n const explain = this.generateExplain();\n this._preamble = explain.preamble;\n if (this.isIntrospection) {\n explain.query += \" -- @qd_introspection\";\n }\n return { commands, query: explain.query };\n }\n\n private generateSetCommands() {\n let commands = \"\";\n for (const key in this.commands) {\n const value = this.commands[key];\n commands += `set local ${key}=${value};\\n`;\n }\n return commands;\n }\n\n private generateExplain() {\n let query = \"\";\n if (this.explainFlags.length > 0) {\n query += `explain (${this.explainFlags.join(\", \")}) `;\n }\n const semicolon = this.query.endsWith(\";\") ? \"\" : \";\";\n\n const preamble = query.length;\n query += `${this.query}${semicolon}`;\n return { query, preamble };\n }\n}\n","/**\n * Represents an identifier in postgres that is subject\n * to quoting rules. The {@link toString} rule behaves\n * exactly like calling `select quote_ident($1)` in postgres\n */\nexport class PgIdentifier {\n private constructor(\n private readonly value: string,\n private readonly quoted: boolean,\n ) {}\n\n /**\n * Constructs an identifier from a single part (column or table name).\n * When quoting identifiers like `select table.col` use {@link fromParts} instead\n */\n static fromString(identifier: string): PgIdentifier {\n const identifierRegex = /^[a-z_][a-zA-Z0-9_]*$/;\n const match = identifier.match(/^\"(.+)\"$/);\n if (match) {\n return new PgIdentifier(match[1], true);\n }\n const quoted =\n !identifierRegex.test(identifier) ||\n this.reservedKeywords.has(identifier.toLowerCase());\n return new PgIdentifier(identifier, quoted);\n }\n\n /**\n * Quotes parts of an identifier like `select schema.table.col`.\n * A separate function is necessary because postgres will treat\n * `select \"HELLO.WORLD\"` as a column name. It has to be like\n * `select \"HELLO\".\"WORLD\"` instead.\n */\n static fromParts(...identifiers: (string | PgIdentifier)[]): PgIdentifier {\n return new PgIdentifier(\n identifiers\n .map((identifier) => {\n if (typeof identifier === \"string\") {\n return PgIdentifier.fromString(identifier);\n } else {\n return identifier;\n }\n })\n .join(\".\"),\n false,\n );\n }\n\n toString(): string {\n if (this.quoted) {\n return `\"${this.value.replace(/\"/g, '\"\"')}\"`;\n }\n return this.value;\n }\n\n toJSON(): string {\n return this.toString();\n }\n\n // Every keyword that's not explicitly marked as\n // unreserved in src/include/parser/kwlist.h\n private static readonly reservedKeywords = new Set([\n \"all\",\n \"analyse\",\n \"analyze\",\n \"and\",\n \"any\",\n \"array\",\n \"as\",\n \"asc\",\n \"asymmetric\",\n \"authorization\",\n \"between\",\n \"bigint\",\n \"binary\",\n \"bit\",\n \"boolean\",\n \"both\",\n \"case\",\n \"cast\",\n \"char\",\n \"character\",\n \"check\",\n \"coalesce\",\n \"collate\",\n \"collation\",\n \"column\",\n \"concurrently\",\n \"constraint\",\n \"create\",\n \"cross\",\n \"current_catalog\",\n \"current_date\",\n \"current_role\",\n \"current_schema\",\n \"current_time\",\n \"current_timestamp\",\n \"current_user\",\n \"dec\",\n \"decimal\",\n \"default\",\n \"deferrable\",\n \"desc\",\n \"distinct\",\n \"do\",\n \"else\",\n \"end\",\n \"except\",\n \"exists\",\n \"extract\",\n \"false\",\n \"fetch\",\n \"float\",\n \"for\",\n \"foreign\",\n \"freeze\",\n \"from\",\n \"full\",\n \"grant\",\n \"greatest\",\n \"group\",\n \"grouping\",\n \"having\",\n \"ilike\",\n \"in\",\n \"initially\",\n \"inner\",\n \"inout\",\n \"int\",\n \"integer\",\n \"intersect\",\n \"interval\",\n \"into\",\n \"is\",\n \"isnull\",\n \"join\",\n \"json\",\n \"json_array\",\n \"json_arrayagg\",\n \"json_exists\",\n \"json_object\",\n \"json_objectagg\",\n \"json_query\",\n \"json_scalar\",\n \"json_serialize\",\n \"json_table\",\n \"json_value\",\n \"lateral\",\n \"leading\",\n \"least\",\n \"left\",\n \"like\",\n \"limit\",\n \"localtime\",\n \"localtimestamp\",\n \"merge_action\",\n \"national\",\n \"natural\",\n \"nchar\",\n \"none\",\n \"normalize\",\n \"not\",\n \"notnull\",\n \"null\",\n \"nullif\",\n \"numeric\",\n \"offset\",\n \"on\",\n \"only\",\n \"or\",\n \"order\",\n \"out\",\n \"outer\",\n \"overlaps\",\n \"overlay\",\n \"placing\",\n \"position\",\n \"precision\",\n \"primary\",\n \"real\",\n \"references\",\n \"returning\",\n \"right\",\n \"row\",\n \"select\",\n \"session_user\",\n \"setof\",\n \"similar\",\n \"smallint\",\n \"some\",\n \"substring\",\n \"symmetric\",\n \"system_user\",\n \"table\",\n \"tablesample\",\n \"then\",\n \"time\",\n \"timestamp\",\n \"to\",\n \"trailing\",\n \"treat\",\n \"trim\",\n \"true\",\n \"union\",\n \"unique\",\n \"user\",\n \"using\",\n \"values\",\n \"varchar\",\n \"variadic\",\n \"verbose\",\n \"when\",\n \"where\",\n \"window\",\n \"with\",\n \"xmlattributes\",\n \"xmlconcat\",\n \"xmlelement\",\n \"xmlexists\",\n \"xmlforest\",\n \"xmlnamespaces\",\n \"xmlparse\",\n \"xmlpi\",\n \"xmlroot\",\n \"xmlserialize\",\n \"xmltable\",\n ]);\n}\n","import type { NullTestType } from \"@pgsql/types\";\nimport { blue, gray, green, magenta, red, yellow } from \"colorette\";\nimport { isIndexProbablyDroppable } from \"src/sql/indexes.js\";\nimport { PgIdentifier } from \"src/sql/pg-identifier.js\";\nimport type { SortContext } from \"../sql/analyzer.js\";\nimport { PostgresQueryBuilder } from \"../sql/builder.js\";\nimport {\n dropIndex,\n type Postgres,\n type PostgresExplainResult,\n type PostgresExplainStage,\n type PostgresTransaction,\n} from \"../sql/database.js\";\nimport type { IndexedTable, Statistics } from \"./statistics.js\";\n\nexport type IndexIdentifier = string;\n\nexport type IndexRecommendation = PermutedIndexCandidate & {\n definition: IndexIdentifier;\n};\n\ntype Color = (a: string) => string;\n\ntype IndexToCreate = PermutedIndexCandidate & {\n name: PgIdentifier;\n definition: IndexIdentifier;\n};\n\nexport class IndexOptimizer {\n static prefix = \"__qd_\";\n\n constructor(\n private readonly db: Postgres,\n private readonly statistics: Statistics,\n private existingIndexes: IndexedTable[],\n private readonly config: {\n trace?: boolean;\n debug?: boolean;\n } = {},\n ) {}\n\n async run(\n builder: PostgresQueryBuilder,\n indexes: RootIndexCandidate[],\n beforeQuery?: (tx: PostgresTransaction) => Promise<void>,\n ): Promise<OptimizeResult> {\n const baseExplain = await this.testQueryWithStats(builder, async (tx) => {\n if (beforeQuery) {\n await beforeQuery(tx);\n }\n });\n const baseCost: number = Number(baseExplain.Plan[\"Total Cost\"]);\n if (baseCost === 0) {\n return {\n kind: \"zero_cost_plan\",\n explainPlan: baseExplain.Plan,\n };\n }\n const toCreate = this.indexesToCreate(indexes);\n const finalExplain = await this.testQueryWithStats(builder, async (tx) => {\n if (beforeQuery) {\n await beforeQuery(tx);\n }\n\n // Then create recommended indexes\n for (const permutation of toCreate) {\n const createIndex = PostgresQueryBuilder.createIndex(\n permutation.definition,\n permutation.name,\n )\n .introspect()\n .build();\n\n await tx.exec(createIndex);\n }\n });\n const finalCost = Number(finalExplain.Plan[\"Total Cost\"]);\n if (this.config.debug) {\n console.dir(finalExplain, { depth: null });\n }\n const deltaPercentage = ((baseCost - finalCost) / baseCost) * 100;\n if (finalCost < baseCost) {\n console.log(\n ` 🎉🎉🎉 ${green(`+${deltaPercentage.toFixed(2).padStart(5, \"0\")}%`)}`,\n );\n } else if (finalCost > baseCost) {\n console.log(\n `${red(\n `-${Math.abs(deltaPercentage).toFixed(2).padStart(5, \"0\")}%`,\n )} ${gray(\"If there's a better index, we haven't tried it\")}`,\n );\n }\n const baseIndexes = this.findUsedIndexes(baseExplain.Plan);\n const finalIndexes = this.findUsedIndexes(finalExplain.Plan);\n const triedIndexes = new Map(\n toCreate.map((index) => [index.name.toString(), index]),\n );\n this.replaceUsedIndexesWithDefinition(finalExplain.Plan, triedIndexes);\n\n return {\n kind: \"ok\",\n baseCost,\n finalCost,\n newIndexes: finalIndexes.newIndexes,\n existingIndexes: baseIndexes.existingIndexes,\n triedIndexes,\n baseExplainPlan: baseExplain.Plan,\n explainPlan: finalExplain.Plan,\n };\n }\n\n async runWithoutIndexes(builder: PostgresQueryBuilder) {\n return await this.testQueryWithStats(builder, async (tx) => {\n await this.dropExistingIndexes(tx);\n });\n }\n\n /**\n * Given the current indexes in the optimizer, transform them in some\n * way to change which indexes will be assumed to exist when optimizing\n *\n * @example\n * ```\n * // resets indexes\n * optimizer.transformIndexes(() => [])\n *\n * // adds new index\n * optimizer.transformIndexes(indexes => [...indexes, newIndex])\n * ```\n */\n transformIndexes(f: (indexes: IndexedTable[]) => IndexedTable[]) {\n const newIndexes = f(this.existingIndexes);\n this.existingIndexes = newIndexes;\n return this;\n }\n\n /**\n * Postgres has a limit of 63 characters for index names.\n * So we use this to make sure we don't derive it from a list of columns that can\n * overflow that limit.\n */\n private indexName(): PgIdentifier {\n const indexName =\n IndexOptimizer.prefix + Math.random().toString(36).substring(2, 16);\n return PgIdentifier.fromString(indexName);\n }\n\n // TODO: this doesn't belong in the optimizer\n private indexAlreadyExists(\n table: string,\n columns: RootIndexCandidate[],\n ): IndexedTable | undefined {\n return this.existingIndexes.find(\n (index) =>\n index.index_type === \"btree\" &&\n index.table_name === table &&\n index.index_columns.length === columns.length &&\n index.index_columns.every((c, i) => columns[i].column === c.name),\n );\n }\n\n /**\n * Derive the list of indexes [tableA(X, Y, Z), tableB(H, I, J)]\n **/\n private indexesToCreate(\n rootCandidates: RootIndexCandidate[],\n ): IndexToCreate[] {\n const permutedIndexes = this.tableColumnIndexCandidates(rootCandidates);\n const nextStage: IndexToCreate[] = [];\n for (const permutation of permutedIndexes.values()) {\n const { table: rawTable, schema: rawSchema, columns } = permutation;\n const permutations = permuteWithFeedback(columns);\n let iter = permutations.next(PROCEED);\n while (!iter.done) {\n const columns = iter.value;\n // TODO: accept PgIdentifier values instead\n // required refactoring `PermutedIndexCandidate`\n const schema = PgIdentifier.fromString(rawSchema);\n const table = PgIdentifier.fromString(rawTable);\n const existingIndex = this.indexAlreadyExists(\n table.toString(),\n columns,\n );\n if (existingIndex) {\n iter = permutations.next(PROCEED);\n continue;\n }\n const indexName = this.indexName();\n\n const definition = this.toDefinition({ table, schema, columns }).raw;\n\n iter = permutations.next(PROCEED);\n nextStage.push({\n name: indexName,\n schema: schema.toString(),\n table: table.toString(),\n columns,\n definition,\n });\n }\n }\n return nextStage;\n }\n\n private toDefinition({\n schema,\n table,\n columns,\n }: {\n schema: PgIdentifier;\n table: PgIdentifier;\n columns: RootIndexCandidate[];\n }) {\n const make = (col: Color, order: Color, where: Color, keyword: Color) => {\n // TODO: refactor all of this class to accept PgIdentifiers\n let fullyQualifiedTable: PgIdentifier;\n\n if (schema.toString() === \"public\") {\n fullyQualifiedTable = table;\n } else {\n fullyQualifiedTable = PgIdentifier.fromParts(schema, table);\n }\n const baseColumn = `${fullyQualifiedTable}(${columns\n .map((c) => {\n const column = PgIdentifier.fromString(c.column);\n const direction = c.sort && this.sortDirection(c.sort);\n const nulls = c.sort && this.nullsOrder(c.sort);\n let sort = col(column.toString());\n if (direction) {\n sort += ` ${order(direction)}`;\n }\n if (nulls) {\n sort += ` ${order(nulls)}`;\n }\n return sort;\n })\n .join(\", \")})`;\n // TODO: add support for generating partial indexes\n // if (clauses.length > 0) {\n // return `${baseColumn} ${where(\"where\")} ${clauses.join(\" and \")}`;\n // }\n return baseColumn;\n };\n const id: Color = (a) => a;\n const raw = make(id, id, id, id);\n const colored = make(green, yellow, magenta, blue);\n return { raw, colored };\n }\n\n /**\n * Drop indexes that can be dropped. Ignore the ones that can't\n */\n private async dropExistingIndexes(tx: PostgresTransaction) {\n for (const index of this.existingIndexes) {\n if (!isIndexProbablyDroppable(index)) {\n continue;\n }\n const indexName = PgIdentifier.fromParts(\n index.schema_name,\n index.index_name,\n );\n await dropIndex(tx, indexName);\n }\n }\n\n private whereClause(c: RootIndexCandidate, col: Color, keyword: Color) {\n if (!c.where) {\n return \"\";\n }\n if (c.where.nulltest === \"IS_NULL\") {\n return `${col(`\"${c.column}\"`)} is ${keyword(\"null\")}`;\n }\n if (c.where.nulltest === \"IS_NOT_NULL\") {\n return `${col(`\"${c.column}\"`)} is not ${keyword(\"null\")}`;\n }\n return \"\";\n }\n\n private nullsOrder(s: SortContext) {\n if (!s.nulls) {\n return \"\";\n }\n switch (s.nulls) {\n case \"SORTBY_NULLS_FIRST\":\n return \"nulls first\";\n case \"SORTBY_NULLS_LAST\":\n return \"nulls last\";\n case \"SORTBY_NULLS_DEFAULT\":\n default:\n return \"\";\n }\n }\n\n private sortDirection(s: SortContext) {\n if (!s.dir) {\n return \"\";\n }\n switch (s.dir) {\n case \"SORTBY_DESC\":\n return \"desc\";\n case \"SORTBY_ASC\":\n return \"asc\";\n case \"SORTBY_DEFAULT\":\n // god help us if we ever run into this\n case \"SORTBY_USING\":\n default:\n return \"\";\n }\n }\n\n async testQueryWithStats(\n builder: PostgresQueryBuilder,\n f?: (tx: PostgresTransaction) => Promise<void>,\n options?: { params?: unknown[]; genericPlan?: boolean },\n ): Promise<{ Plan: PostgresExplainStage }> {\n try {\n await this.db.transaction(async (tx) => {\n await f?.(tx);\n await this.statistics.restoreStats(tx);\n const flags = [\"format json\"];\n if (options && !options.genericPlan) {\n flags.push(\"analyze\");\n if (this.config.trace) {\n // trace can only be used alongside analyze\n // since it depends on the results of the query execution\n flags.push(\"trace\");\n }\n } else {\n flags.push(\"generic_plan\");\n }\n const { commands, query } = builder.explain(flags).buildParts();\n // this is done in a separate step to prevent sending multiple commands when using parameters\n await tx.exec(commands);\n const result = await tx.exec<PostgresExplainResult>(\n query,\n options?.params,\n );\n const explain = result[0][\"QUERY PLAN\"][0];\n throw new RollbackError(explain);\n });\n } catch (error) {\n if (error instanceof RollbackError) {\n return error.value;\n }\n throw error;\n }\n throw new Error(\"Unreachable\");\n }\n\n private tableColumnIndexCandidates(indexes: RootIndexCandidate[]) {\n const tableColumns: Map<\n string,\n { schema: string; table: string; columns: RootIndexCandidate[] }\n > = new Map();\n for (const index of indexes) {\n const existing = tableColumns.get(`${index.schema}.${index.table}`);\n if (existing) {\n existing.columns.push(index);\n } else {\n tableColumns.set(`${index.schema}.${index.table}`, {\n table: index.table,\n schema: index.schema,\n columns: [index],\n });\n }\n }\n return tableColumns;\n }\n\n private findUsedIndexes(explain: Record<string, any>) {\n const newIndexes: Set<string> = new Set();\n const existingIndexes: Set<string> = new Set();\n const prefix = IndexOptimizer.prefix;\n walkExplain(explain, (stage) => {\n const indexName = stage[\"Index Name\"];\n if (indexName) {\n // Check for prefix at start or embedded (for hypertable chunk indexes like _hyper_1_1_chunk___qd_xxx)\n if (indexName.startsWith(prefix)) {\n newIndexes.add(indexName);\n } else if (indexName.includes(prefix)) {\n // Extract the actual index name from chunk-prefixed names (e.g., _hyper_1_1_chunk___qd_xxx -> __qd_xxx)\n const actualName = indexName.substring(indexName.indexOf(prefix));\n newIndexes.add(actualName);\n } else {\n existingIndexes.add(indexName);\n }\n }\n });\n return {\n newIndexes,\n existingIndexes,\n };\n }\n\n private replaceUsedIndexesWithDefinition(\n explain: Record<string, any>,\n triedIndexes: Map<string, IndexRecommendation>,\n ) {\n walkExplain(explain, (stage) => {\n const indexName = stage[\"Index Name\"];\n if (indexName) {\n const recommendation = triedIndexes.get(indexName);\n if (recommendation) {\n stage[\"Index Name\"] = recommendation.definition;\n }\n }\n });\n }\n}\n\nfunction walkExplain(explain: Record<string, any>, f: (stage: any) => void) {\n function go(plan: any) {\n f(plan);\n if (plan.Plans) {\n for (const p of plan.Plans) {\n go(p);\n }\n }\n }\n}\n\nexport type OptimizeResult =\n | {\n kind: \"ok\";\n baseExplainPlan: PostgresExplainStage;\n baseCost: number;\n finalCost: number;\n newIndexes: Set<string>;\n existingIndexes: Set<string>;\n triedIndexes: Map<string, IndexRecommendation>;\n explainPlan: PostgresExplainStage;\n }\n | {\n kind: \"zero_cost_plan\";\n explainPlan: PostgresExplainStage;\n };\n\nclass RollbackError<T> {\n constructor(public readonly value?: T) {}\n}\n\nexport type RootIndexCandidate = {\n schema: string;\n table: string;\n column: string;\n sort?: SortContext;\n where?: { nulltest?: NullTestType };\n};\n\nexport type PermutedIndexCandidate = {\n schema: string;\n table: string;\n columns: RootIndexCandidate[];\n // TODO: functional indexes\n where?: string;\n};\n\nexport const PROCEED = Symbol(\"PROCEED\");\nexport const SKIP = Symbol(\"SKIP\");\n\n/**\n * Allows permuting over an array of items.\n * The generator allows the caller to prematurely stop the permutation chain.\n */\nexport function* permuteWithFeedback<T>(\n arr: T[],\n): Generator<T[], void, typeof PROCEED | typeof SKIP> {\n function* helper(\n path: T[],\n rest: T[],\n ): Generator<T[], void, typeof PROCEED | typeof SKIP> {\n let i = 0;\n while (i < rest.length) {\n const nextPath = [...path, rest[i]];\n const nextRest = [...rest.slice(0, i), ...rest.slice(i + 1)];\n const input = yield nextPath;\n\n if (input === PROCEED) {\n yield* helper(nextPath, nextRest);\n }\n\n i++;\n }\n }\n\n yield* helper([], arr);\n}\n","import { gray } from \"colorette\";\nimport dedent from \"dedent\";\nimport { z } from \"zod\";\nimport type {\n Postgres,\n PostgresTransaction,\n PostgresVersion,\n} from \"../sql/database.ts\";\n\ntype StaValueKind = \"real\" | \"text\" | \"boolean\" | null;\n\nexport type Path = string;\n\nexport const StatisticsSource = z.union([\n z.object({\n kind: z.literal(\"path\"),\n path: z.string().min(1),\n }),\n z.object({\n kind: z.literal(\"inline\"),\n }),\n]);\n\nexport const ExportedStatsStatistics = z.object({\n stawidth: z.number(),\n stainherit: z.boolean().default(false),\n // 0 representing unknown\n stadistinct: z.number(),\n // this has no \"nullable\" state\n stanullfrac: z.number(),\n stakind1: z.number().min(0),\n stakind2: z.number().min(0),\n stakind3: z.number().min(0),\n stakind4: z.number().min(0),\n stakind5: z.number().min(0),\n staop1: z.string(),\n staop2: z.string(),\n staop3: z.string(),\n staop4: z.string(),\n staop5: z.string(),\n stacoll1: z.string(),\n stacoll2: z.string(),\n stacoll3: z.string(),\n stacoll4: z.string(),\n stacoll5: z.string(),\n stanumbers1: z.array(z.number()).nullable(),\n stanumbers2: z.array(z.number()).nullable(),\n stanumbers3: z.array(z.number()).nullable(),\n stanumbers4: z.array(z.number()).nullable(),\n stanumbers5: z.array(z.number()).nullable(),\n // theoretically... this could only be strings and numbers\n // but we don't have a crystal ball\n stavalues1: z.array(z.any()).nullable(),\n stavalues2: z.array(z.any()).nullable(),\n stavalues3: z.array(z.any()).nullable(),\n stavalues4: z.array(z.any()).nullable(),\n stavalues5: z.array(z.any()).nullable(),\n});\n\nexport const ExportedStatsColumns = z.object({\n columnName: z.string(),\n stats: ExportedStatsStatistics.nullable(),\n});\n\nexport const ExportedStatsIndex = z.object({\n indexName: z.string(),\n relpages: z.number(),\n reltuples: z.number(),\n relallvisible: z.number(),\n relallfrozen: z.number().optional(),\n});\n\n// This should match the output of the `_qd_dump_stats` function in the analyzer README.md\n// Need to make sure this is versioned to accept ALL potential outputs from every version of\n// dump functions we make public\nexport const ExportedStatsV1 = z.object({\n tableName: z.string(),\n schemaName: z.string(),\n // can be negative\n relpages: z.number(),\n // can be negative\n reltuples: z.number(),\n relallvisible: z.number(),\n // only postgres 18+\n relallfrozen: z.number().optional(),\n columns: z.array(ExportedStatsColumns).nullable(),\n indexes: z.array(ExportedStatsIndex),\n});\n\nexport const ExportedStats = z.union([ExportedStatsV1]);\n\nexport type ExportedStats = z.infer<typeof ExportedStats>;\n\nexport const StatisticsMode = z.discriminatedUnion(\"kind\", [\n z.object({\n kind: z.literal(\"fromAssumption\"),\n reltuples: z.number().min(0),\n relpages: z.number().min(0),\n }),\n z.object({\n kind: z.literal(\"fromStatisticsExport\"),\n stats: z.array(ExportedStats),\n source: StatisticsSource,\n }),\n]);\n\nexport type StatisticsMode = z.infer<typeof StatisticsMode>;\n\nconst DEFAULT_RELTUPLES = 10_000;\nconst DEFAULT_RELPAGES = 1;\nexport class Statistics {\n readonly mode: StatisticsMode;\n private readonly exportedMetadata: ExportedStats[] | undefined;\n // preventing accidental internal mutations\n static readonly defaultStatsMode: StatisticsMode = Object.freeze({\n kind: \"fromAssumption\",\n reltuples: DEFAULT_RELTUPLES,\n relpages: DEFAULT_RELPAGES,\n });\n constructor(\n private readonly db: Postgres,\n public readonly postgresVersion: PostgresVersion,\n public readonly ownMetadata: ExportedStats[],\n statsMode: StatisticsMode,\n ) {\n if (statsMode) {\n this.mode = statsMode;\n if (statsMode.kind === \"fromStatisticsExport\") {\n this.exportedMetadata = statsMode.stats;\n }\n } else {\n this.mode = Statistics.defaultStatsMode;\n }\n }\n\n static statsModeFromAssumption({\n reltuples,\n relpages,\n }: {\n reltuples: number;\n relpages: number;\n }): StatisticsMode {\n return {\n kind: \"fromAssumption\",\n reltuples,\n relpages,\n };\n }\n\n /**\n * Create a statistic mode from stats exported from another database\n **/\n static statsModeFromExport(stats: ExportedStats[]): StatisticsMode {\n return {\n kind: \"fromStatisticsExport\",\n source: { kind: \"inline\" },\n stats,\n };\n }\n\n static async fromPostgres(\n db: Postgres,\n statsMode: StatisticsMode,\n ): Promise<Statistics> {\n const version = await db.serverNum();\n const ownStats = await Statistics.dumpStats(db, version, \"full\");\n return new Statistics(db, version, ownStats, statsMode);\n }\n\n restoreStats(tx: PostgresTransaction) {\n // if (this.postgresVersion < \"180000\") {\n return this.restoreStats17(tx);\n // }\n // return this.restoreStats18(tx);\n }\n\n approximateTotalRows() {\n if (!this.exportedMetadata) {\n return 0;\n }\n let totalRows = 0;\n for (const table of this.exportedMetadata) {\n totalRows += table.reltuples;\n }\n return totalRows;\n }\n\n /**\n * We have to cast stavaluesN to the correct type\n * This derives that type for us so it can be used in `array_in`\n */\n private stavalueKind(values: unknown[] | null): StaValueKind {\n if (!values || values.length === 0) {\n return null;\n }\n const [elem] = values;\n if (typeof elem === \"number\") {\n return \"real\";\n } else if (typeof elem === \"boolean\") {\n return \"boolean\";\n }\n // is everything else a text? What about strinfied dates?\n // we might need column metadata access here if we do\n return \"text\";\n }\n\n private async restoreStats17(tx: PostgresTransaction) {\n const warnings = {\n tablesNotInExports: [] as string[],\n tablesNotInTest: [] as string[],\n tableNotAnalyzed: [] as string[],\n statsMissing: [] as {\n statistic: string;\n table: string;\n schema: string;\n column: string;\n }[],\n };\n const processedTables = new Set<string>();\n\n let columnStatsUpdatePromise: Promise<any> | undefined;\n const columnStatsValues: Array<{\n schema_name: string;\n table_name: string;\n column_name: string;\n stainherit: boolean;\n stanullfrac: number;\n stawidth: number;\n stadistinct: number;\n stakind1: number;\n stakind2: number;\n stakind3: number;\n stakind4: number;\n stakind5: number;\n staop1: string;\n staop2: string;\n staop3: string;\n staop4: string;\n staop5: string;\n stacoll1: string;\n stacoll2: string;\n stacoll3: string;\n stacoll4: string;\n stacoll5: string;\n stanumbers1: number[] | null;\n stanumbers2: number[] | null;\n stanumbers3: number[] | null;\n stanumbers4: number[] | null;\n stanumbers5: number[] | null;\n stavalues1: any[] | null;\n stavalues2: any[] | null;\n stavalues3: any[] | null;\n stavalues4: any[] | null;\n stavalues5: any[] | null;\n _value_type1: StaValueKind;\n _value_type2: StaValueKind;\n _value_type3: StaValueKind;\n _value_type4: StaValueKind;\n _value_type5: StaValueKind;\n }> = [];\n if (this.exportedMetadata) {\n for (const table of this.ownMetadata) {\n const targetTable = this.exportedMetadata.find(\n (m) =>\n m.tableName === table.tableName &&\n m.schemaName === table.schemaName,\n );\n if (!targetTable?.columns) {\n continue;\n }\n for (const column of targetTable.columns) {\n const { stats } = column;\n if (!stats) {\n continue;\n }\n // TODO: track processed columns too\n columnStatsValues.push({\n schema_name: table.schemaName,\n table_name: table.tableName,\n column_name: column.columnName,\n stainherit: stats.stainherit ?? false,\n stanullfrac: stats.stanullfrac,\n stawidth: stats.stawidth,\n stadistinct: stats.stadistinct,\n stakind1: stats.stakind1,\n stakind2: stats.stakind2,\n stakind3: stats.stakind3,\n stakind4: stats.stakind4,\n stakind5: stats.stakind5,\n staop1: stats.staop1,\n staop2: stats.staop2,\n staop3: stats.staop3,\n staop4: stats.staop4,\n staop5: stats.staop5,\n stacoll1: stats.stacoll1,\n stacoll2: stats.stacoll2,\n stacoll3: stats.stacoll3,\n stacoll4: stats.stacoll4,\n stacoll5: stats.stacoll5,\n stanumbers1: stats.stanumbers1,\n stanumbers2: stats.stanumbers2,\n stanumbers3: stats.stanumbers3,\n stanumbers4: stats.stanumbers4,\n stanumbers5: stats.stanumbers5,\n stavalues1: stats.stavalues1,\n stavalues2: stats.stavalues2,\n stavalues3: stats.stavalues3,\n stavalues4: stats.stavalues4,\n stavalues5: stats.stavalues5,\n _value_type1: this.stavalueKind(stats.stavalues1),\n _value_type2: this.stavalueKind(stats.stavalues2),\n _value_type3: this.stavalueKind(stats.stavalues3),\n _value_type4: this.stavalueKind(stats.stavalues4),\n _value_type5: this.stavalueKind(stats.stavalues5),\n });\n }\n }\n /**\n * Postgres has 5 different slots for storing statistics per column and a potentially unlimited\n * number of statistic types to choose from. Each code in `stakindN` can mean different things.\n * Some statistics are just numerical values such as `n_distinct` and `correlation`, meaning\n * they're only derived from `stanumbersN` and the value of `stanumbersN` is never read.\n * Others take advantage of the `stavaluesN` columns which use `anyarray` type to store\n * concrete values internally for things like histogram bounds.\n * Unfortunately we cannot change anyarrays without a C extension.\n *\n * (1) = most common values\n * (2) = scalar histogram\n * (3) = correlation <- can change\n * (4) = most common elements\n * (5) = distinct elem count histogram <- can change\n * (6) = length histogram (?) These don't appear in pg_stats\n * (7) = bounds histogram (?) These don't appear in pg_stats\n * (N) = potentially many more kinds of statistics. But postgres <=18 only uses these 7.\n *\n * What we're doing here is setting ANY statistic we cannot directly control\n * (anything that relies on stavaluesN) to 0 to make sure the planner isn't influenced by what\n * what the db collected from the test data.\n * Because we do our tests with `generic_plan` it seems it's already unlikely that the planner will be\n * using things like common values or histogram bounds to make the planning decisions we care about.\n * This is a just in case.\n */\n const sql = dedent`\n WITH input AS (\n SELECT\n c.oid AS starelid,\n a.attnum AS staattnum,\n v.stainherit,\n v.stanullfrac,\n v.stawidth,\n v.stadistinct,\n v.stakind1,\n v.stakind2,\n v.stakind3,\n v.stakind4,\n v.stakind5,\n v.staop1,\n v.staop2,\n v.staop3,\n v.staop4,\n v.staop5,\n v.stacoll1,\n v.stacoll2,\n v.stacoll3,\n v.stacoll4,\n v.stacoll5,\n v.stanumbers1,\n v.stanumbers2,\n v.stanumbers3,\n v.stanumbers4,\n v.stanumbers5,\n v.stavalues1,\n v.stavalues2,\n v.stavalues3,\n v.stavalues4,\n v.stavalues5,\n _value_type1,\n _value_type2,\n _value_type3,\n _value_type4,\n _value_type5\n FROM jsonb_to_recordset($1::jsonb) AS v(\n schema_name text,\n table_name text,\n column_name text,\n stainherit boolean,\n stanullfrac real,\n stawidth integer,\n stadistinct real,\n stakind1 real,\n stakind2 real,\n stakind3 real,\n stakind4 real,\n stakind5 real,\n staop1 oid,\n staop2 oid,\n staop3 oid,\n staop4 oid,\n staop5 oid,\n stacoll1 oid,\n stacoll2 oid,\n stacoll3 oid,\n stacoll4 oid,\n stacoll5 oid,\n stanumbers1 real[],\n stanumbers2 real[],\n stanumbers3 real[],\n stanumbers4 real[],\n stanumbers5 real[],\n stavalues1 text[],\n stavalues2 text[],\n stavalues3 text[],\n stavalues4 text[],\n stavalues5 text[],\n _value_type1 text,\n _value_type2 text,\n _value_type3 text,\n _value_type4 text,\n _value_type5 text\n )\n JOIN pg_class c ON c.relname = v.table_name\n JOIN pg_namespace n ON n.oid = c.relnamespace AND n.nspname = v.schema_name\n JOIN pg_attribute a ON a.attrelid = c.oid AND a.attname = v.column_name\n ),\n updated AS (\n UPDATE pg_statistic s\n SET\n stanullfrac = i.stanullfrac,\n stawidth = i.stawidth,\n stadistinct = i.stadistinct,\n stakind1 = i.stakind1,\n stakind2 = i.stakind2,\n stakind3 = i.stakind3,\n stakind4 = i.stakind4,\n stakind5 = i.stakind5,\n staop1 = i.staop1,\n staop2 = i.staop2,\n staop3 = i.staop3,\n staop4 = i.staop4,\n staop5 = i.staop5,\n stacoll1 = i.stacoll1,\n stacoll2 = i.stacoll2,\n stacoll3 = i.stacoll3,\n stacoll4 = i.stacoll4,\n stacoll5 = i.stacoll5,\n stanumbers1 = i.stanumbers1,\n stanumbers2 = i.stanumbers2,\n stanumbers3 = i.stanumbers3,\n stanumbers4 = i.stanumbers4,\n stanumbers5 = i.stanumbers5,\n stavalues1 = case\n when i.stavalues1 is null then null\n else array_in(i.stavalues1::text::cstring, i._value_type1::regtype::oid, -1)\n end,\n stavalues2 = case\n when i.stavalues2 is null then null\n else array_in(i.stavalues2::text::cstring, i._value_type2::regtype::oid, -1)\n end,\n stavalues3 = case\n when i.stavalues3 is null then null\n else array_in(i.stavalues3::text::cstring, i._value_type3::regtype::oid, -1)\n end,\n stavalues4 = case\n when i.stavalues4 is null then null\n else array_in(i.stavalues4::text::cstring, i._value_type4::regtype::oid, -1)\n end,\n stavalues5 = case\n when i.stavalues5 is null then null\n else array_in(i.stavalues5::text::cstring, i._value_type5::regtype::oid, -1)\n end\n -- stavalues1 = i.stavalues1,\n -- stavalues2 = i.stavalues2,\n -- stavalues3 = i.stavalues3,\n -- stavalues4 = i.stavalues4,\n -- stavalues5 = i.stavalues5\n FROM input i\n WHERE s.starelid = i.starelid AND s.staattnum = i.staattnum AND s.stainherit = i.stainherit\n RETURNING s.starelid, s.staattnum, s.stainherit, s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5\n ),\n inserted as (\n INSERT INTO pg_statistic (\n starelid, staattnum, stainherit,\n stanullfrac, stawidth, stadistinct,\n stakind1, stakind2, stakind3, stakind4, stakind5,\n staop1, staop2, staop3, staop4, staop5,\n stacoll1, stacoll2, stacoll3, stacoll4, stacoll5,\n stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5,\n stavalues1, stavalues2, stavalues3, stavalues4, stavalues5\n )\n SELECT\n i.starelid, i.staattnum, i.stainherit,\n i.stanullfrac, i.stawidth, i.stadistinct,\n i.stakind1, i.stakind2, i.stakind3, i.stakind4, i.stakind5,\n i.staop1, i.staop2, i.staop3, i.staop4, i.staop5,\n i.stacoll1, i.stacoll2, i.stacoll3, i.stacoll4, i.stacoll5,\n i.stanumbers1, i.stanumbers2, i.stanumbers3, i.stanumbers4, i.stanumbers5,\n -- i.stavalues1, i.stavalues2, i.stavalues3, i.stavalues4, i.stavalues5,\n case\n when i.stavalues1 is null then null\n else array_in(i.stavalues1::text::cstring, i._value_type1::regtype::oid, -1)\n end,\n case\n when i.stavalues2 is null then null\n else array_in(i.stavalues2::text::cstring, i._value_type2::regtype::oid, -1)\n end,\n case\n when i.stavalues3 is null then null\n else array_in(i.stavalues3::text::cstring, i._value_type3::regtype::oid, -1)\n end,\n case\n when i.stavalues4 is null then null\n else array_in(i.stavalues4::text::cstring, i._value_type4::regtype::oid, -1)\n end,\n case\n when i.stavalues5 is null then null\n else array_in(i.stavalues5::text::cstring, i._value_type5::regtype::oid, -1)\n end\n -- i._value_type1, i._value_type2, i._value_type3, i._value_type4, i._value_type5\n FROM input i\n LEFT JOIN updated u\n ON i.starelid = u.starelid AND i.staattnum = u.staattnum AND i.stainherit = u.stainherit\n WHERE u.starelid IS NULL\n returning starelid, staattnum, stainherit, stakind1, stakind2, stakind3, stakind4, stakind5\n )\n select * from updated union all (select * from inserted); -- @qd_introspection`;\n\n columnStatsUpdatePromise = tx\n .exec(sql, [columnStatsValues])\n .catch((err) => {\n console.error(\"Something wrong wrong updating column stats\");\n console.error(err);\n throw err;\n // return err;\n // return Promise.reject(err)\n });\n }\n\n const reltuplesValues: Array<{\n reltuples: number;\n relpages: number;\n relname: string;\n schema_name: string;\n relallvisible: number;\n relallfrozen?: number;\n }> = [];\n for (const table of this.ownMetadata) {\n if (!table.columns) {\n continue;\n }\n processedTables.add(`${table.schemaName}.${table.tableName}`);\n let targetTable: ExportedStats | undefined;\n if (this.exportedMetadata) {\n targetTable = this.exportedMetadata.find(\n (m) =>\n m.tableName === table.tableName &&\n m.schemaName === table.schemaName,\n );\n }\n let reltuples: number;\n let relpages: number;\n let relallvisible: number = 0;\n let relallfrozen: number | undefined;\n if (targetTable) {\n // don't want to run our prod stats with -1 reltuples\n // we warn the user about this later\n // if (targetTable.reltuples < 10 || targetTable.reltuples > 10000) {\n reltuples = targetTable.reltuples;\n relpages = targetTable.relpages;\n relallvisible = targetTable.relallvisible;\n relallfrozen = targetTable.relallfrozen;\n // }\n } else if (this.mode.kind === \"fromAssumption\") {\n reltuples = this.mode.reltuples;\n relpages = this.mode.relpages;\n } else {\n // we want to warn about tables that are in the test but not in the exported stats\n // this can happen in case a new table is created in a PR\n warnings.tablesNotInExports.push(\n `${table.schemaName}.${table.tableName}`,\n );\n reltuples = DEFAULT_RELTUPLES;\n relpages = DEFAULT_RELPAGES;\n }\n reltuplesValues.push({\n relname: table.tableName,\n schema_name: table.schemaName,\n reltuples,\n relpages,\n relallfrozen,\n relallvisible,\n });\n if (targetTable && targetTable.indexes) {\n for (const index of targetTable.indexes) {\n reltuplesValues.push({\n relname: index.indexName,\n schema_name: targetTable.schemaName,\n reltuples: index.reltuples,\n relpages: index.relpages,\n relallfrozen: index.relallfrozen,\n relallvisible: index.relallvisible,\n });\n }\n }\n }\n\n const reltuplesQuery = dedent`\n update pg_class p\n set reltuples = v.reltuples,\n relpages = v.relpages,\n -- relallfrozen = case when v.relallfrozen is null then p.relallfrozen else v.relallfrozen end,\n relallvisible = case when v.relallvisible is null then p.relallvisible else v.relallvisible end\n from jsonb_to_recordset($1::jsonb)\n as v(reltuples real, relpages integer, relallfrozen integer, relallvisible integer, relname text, schema_name text)\n where p.relname = v.relname\n and p.relnamespace = (select oid from pg_namespace where nspname = v.schema_name)\n returning p.relname, p.relnamespace, p.reltuples, p.relpages;\n `;\n\n const reltuplesPromise = tx\n .exec(reltuplesQuery, [reltuplesValues])\n .catch((err) => {\n console.error(\"Something went wrong updating reltuples/relpages\");\n console.error(err);\n return err;\n });\n\n if (this.exportedMetadata) {\n for (const table of this.exportedMetadata) {\n const tableExists = processedTables.has(\n `${table.schemaName}.${table.tableName}`,\n );\n if (tableExists && table.reltuples === -1) {\n console.warn(\n `Table ${table.tableName} has reltuples -1. Your production database is probably not analyzed properly`,\n );\n // we expect production stats to have real numbers\n warnings.tableNotAnalyzed.push(\n `${table.schemaName}.${table.tableName}`,\n );\n }\n if (tableExists) {\n continue;\n }\n // there's a LOT of tables in statistics exports for things like timescaledb\n // that might not show up in the test data. This check might be too strict.\n warnings.tablesNotInTest.push(`${table.schemaName}.${table.tableName}`);\n }\n }\n const [statsUpdates, reltuplesUpdates] = await Promise.all([\n columnStatsUpdatePromise,\n reltuplesPromise,\n ]);\n const updatedColumnsProperly = statsUpdates\n ? statsUpdates.length === columnStatsValues.length\n : true;\n if (!updatedColumnsProperly) {\n console.error(`Did not update expected column stats`);\n }\n if (reltuplesUpdates.length !== reltuplesValues.length) {\n console.error(`Did not update expected reltuples/relpages`);\n }\n return warnings;\n }\n\n static async dumpStats(\n db: PostgresTransaction,\n postgresVersion: PostgresVersion,\n kind: \"anonymous\" | \"full\",\n ): Promise<ExportedStats[]> {\n const fullDump = kind === \"full\";\n console.log(`dumping stats for postgres ${gray(postgresVersion)}`);\n // certain things are only supported with pg17\n const stats = await db.exec<{ json_agg: ExportedStats[] }>(\n `\n WITH table_columns AS (\n SELECT\n c.table_name,\n c.table_schema,\n cl.reltuples,\n cl.relpages,\n cl.relallvisible,\n -- cl.relallfrozen,\n n.nspname AS schema_name,\n json_agg(\n json_build_object(\n 'columnName', c.column_name,\n 'stats', (\n SELECT json_build_object(\n 'starelid', s.starelid,\n 'staattnum', s.staattnum,\n 'stanullfrac', s.stanullfrac,\n 'stawidth', s.stawidth,\n 'stadistinct', s.stadistinct,\n 'stakind1', s.stakind1, 'staop1', s.staop1, 'stacoll1', s.stacoll1, 'stanumbers1', s.stanumbers1,\n 'stakind2', s.stakind2, 'staop2', s.staop2, 'stacoll2', s.stacoll2, 'stanumbers2', s.stanumbers2,\n 'stakind3', s.stakind3, 'staop3', s.staop3, 'stacoll3', s.stacoll3, 'stanumbers3', s.stanumbers3,\n 'stakind4', s.stakind4, 'staop4', s.staop4, 'stacoll4', s.stacoll4, 'stanumbers4', s.stanumbers4,\n 'stakind5', s.stakind5, 'staop5', s.staop5, 'stacoll5', s.stacoll5, 'stanumbers5', s.stanumbers5,\n 'stavalues1', CASE WHEN $1 THEN s.stavalues1 ELSE NULL END,\n 'stavalues2', CASE WHEN $1 THEN s.stavalues2 ELSE NULL END,\n 'stavalues3', CASE WHEN $1 THEN s.stavalues3 ELSE NULL END,\n 'stavalues4', CASE WHEN $1 THEN s.stavalues4 ELSE NULL END,\n 'stavalues5', CASE WHEN $1 THEN s.stavalues5 ELSE NULL END\n )\n FROM pg_statistic s\n WHERE s.starelid = a.attrelid AND s.staattnum = a.attnum\n )\n )\n ORDER BY c.ordinal_position\n ) AS columns\n FROM information_schema.columns c\n JOIN pg_attribute a\n ON a.attrelid = (quote_ident(c.table_schema) || '.' || quote_ident(c.table_name))::regclass\n AND a.attname = c.column_name\n JOIN pg_class cl\n ON cl.oid = a.attrelid\n JOIN pg_namespace n\n ON n.oid = cl.relnamespace\n WHERE c.table_name NOT LIKE 'pg_%'\n AND n.nspname <> 'information_schema'\n AND c.table_name NOT IN ('pg_stat_statements', 'pg_stat_statements_info')\n GROUP BY c.table_name, c.table_schema, cl.reltuples, cl.relpages, cl.relallvisible, n.nspname\n ),\n table_indexes AS (\n SELECT\n t.relname AS table_name,\n json_agg(\n json_build_object(\n 'indexName', i.relname,\n 'reltuples', i.reltuples,\n 'relpages', i.relpages,\n 'relallvisible', i.relallvisible\n -- 'relallfrozen', i.relallfrozen\n )\n ) AS indexes\n FROM pg_class t\n JOIN pg_index ix ON ix.indrelid = t.oid\n JOIN pg_class i ON i.oid = ix.indexrelid\n JOIN pg_namespace n ON n.oid = t.relnamespace\n WHERE t.relname NOT LIKE 'pg_%'\n AND n.nspname <> 'information_schema'\n GROUP BY t.relname\n )\n SELECT json_agg(\n json_build_object(\n 'tableName', tc.table_name,\n 'schemaName', tc.table_schema,\n 'reltuples', tc.reltuples,\n 'relpages', tc.relpages,\n 'relallvisible', tc.relallvisible,\n -- 'relallfrozen', tc.relallfrozen,\n 'columns', tc.columns,\n 'indexes', COALESCE(ti.indexes, '[]'::json)\n )\n )\n FROM table_columns tc\n LEFT JOIN table_indexes ti\n ON ti.table_name = tc.table_name;\n `,\n [fullDump],\n );\n return stats[0].json_agg;\n }\n\n /**\n * Returns all indexes in the database.\n * ONLY handles regular btree indexes\n */\n async getExistingIndexes(): Promise<IndexedTable[]> {\n const indexes = await this.db.exec<IndexedTable>(`\n WITH partitioned_tables AS (\n SELECT\n inhparent::regclass AS parent_table,\n inhrelid::regclass AS partition_table\n FROM\n pg_inherits\n )\n SELECT\n n.nspname AS schema_name,\n COALESCE(pt.parent_table::text, t.relname) AS table_name,\n i.relname AS index_name,\n ix.indisprimary as is_primary,\n ix.indisunique as is_unique,\n am.amname AS index_type,\n array_agg(\n CASE\n -- Handle regular columns\n WHEN a.attname IS NOT NULL THEN\n json_build_object('name', a.attname, 'order',\n CASE\n WHEN (indoption[array_position(ix.indkey, a.attnum)] & 1) = 1 THEN 'DESC'\n ELSE 'ASC'\n END)\n -- Handle expressions\n ELSE\n json_build_object('name', pg_get_expr((ix.indexprs)::pg_node_tree, t.oid), 'order',\n CASE\n WHEN (indoption[array_position(ix.indkey, k.attnum)] & 1) = 1 THEN 'DESC'\n ELSE 'ASC'\n END)\n END\n ORDER BY array_position(ix.indkey, k.attnum)\n ) AS index_columns\n FROM\n pg_class t\n LEFT JOIN partitioned_tables pt ON t.oid = pt.partition_table\n JOIN pg_index ix ON t.oid = ix.indrelid\n JOIN pg_class i ON i.oid = ix.indexrelid\n JOIN pg_am am ON i.relam = am.oid\n LEFT JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY k(attnum, ordinality) ON true\n LEFT JOIN pg_attribute a ON a.attnum = k.attnum AND a.attrelid = t.oid\n JOIN pg_namespace n ON t.relnamespace = n.oid\n WHERE\n n.nspname not like 'pg_%' and\n n.nspname <> 'information_schema'\n GROUP BY\n n.nspname, COALESCE(pt.parent_table::text, t.relname), i.relname, am.amname, ix.indisprimary, ix.indisunique\n ORDER BY\n COALESCE(pt.parent_table::text, t.relname), i.relname; -- @qd_introspection\n `);\n return indexes;\n }\n}\n\nexport type ColumnMetadata = {\n columnName: string;\n dataType: string;\n isNullable: boolean;\n stats: ColumnStats | null;\n};\n\ntype ColumnStats = {\n stainherit: boolean;\n stanullfrac: number;\n stawidth: number;\n stadistinct: number;\n stakind1: number;\n stakind2: number;\n stakind3: number;\n stakind4: number;\n stakind5: number;\n staop1: number;\n staop2: number;\n staop3: number;\n staop4: number;\n staop5: number;\n stacoll1: number;\n stacoll2: number;\n stacoll3: number;\n stacoll4: number;\n stacoll5: number;\n stanumbers1: number;\n stanumbers2: number;\n stanumbers3: number;\n stanumbers4: number;\n stanumbers5: number;\n};\n\nexport type TableMetadata = {\n tableName: string;\n schemaName: string;\n reltuples: number;\n relpages: number;\n relallvisible: number;\n relallfrozen?: number;\n columns: ColumnMetadata[];\n};\n\ntype TableName = string;\nexport type TableStats = {\n tupleEstimate: bigint;\n pageCount: number;\n};\n\nexport type SerializeResult = {\n schema: TableMetadata[];\n serialized: string;\n sampledRecords: Record<TableName, number>;\n};\n\nexport type IndexOrder = \"ASC\" | \"DESC\";\n\nexport type IndexedTable = {\n index_columns: Array<{ name: string; order: IndexOrder }>;\n is_primary: boolean;\n is_unique: boolean;\n index_name: string;\n // eslint-disable-next-line @typescript-eslint/ban-types\n index_type: \"btree\" | \"gin\" | (string & {});\n // this is always public\n schema_name: string;\n table_name: string;\n};\n","/**\n * Rewriter for pg_stat_statements queries.\n * Not all queries found in pg_stat_statements can be\n * directly sent back to the database without first being rewritten.\n */\nexport class PssRewriter {\n rewrite(query: string): string {\n return this.rewriteKeywordWithParameter(query);\n }\n\n private rewriteKeywordWithParameter(query: string): string {\n return query.replace(/\\b(\\w+) (\\$\\d+)\\b/gi, (match) => {\n const [keyword, parameter] = match.split(\" \");\n const isProblematicKeyword = this.problematicKeywords.includes(\n keyword.toLowerCase(),\n );\n if (!isProblematicKeyword) {\n return match;\n }\n return `(${parameter}::${keyword.toLowerCase()})`;\n });\n }\n\n private problematicKeywords = [\"interval\", \"timestamp\", \"geometry\"];\n}\n"],"mappings":"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EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,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","names":["is","isANode","import_colorette","columns","import_colorette","import_zod","dedent"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/sql/analyzer.ts","../src/sql/walker.ts","../src/sql/nudges.ts","../src/sql/database.ts","../src/sql/indexes.ts","../src/sql/builder.ts","../src/sql/pg-identifier.ts","../src/optimizer/genalgo.ts","../src/optimizer/statistics.ts","../src/optimizer/pss-rewriter.ts"],"sourcesContent":["export * from \"./sql/analyzer.js\";\nexport * from \"./sql/database.js\";\nexport * from \"./sql/nudges.js\";\nexport * from \"./sql/indexes.js\";\nexport * from \"./sql/builder.js\";\nexport * from \"./sql/pg-identifier.js\";\nexport * from \"./optimizer/genalgo.js\";\nexport * from \"./optimizer/statistics.js\";\nexport * from \"./optimizer/pss-rewriter.js\";\n","import type {\n NullTestType,\n ParseResult,\n SortByDir,\n SortByNulls,\n} from \"@pgsql/types\";\nimport {\n bgMagentaBright,\n blue,\n type Color,\n dim,\n strikethrough,\n} from \"colorette\";\nimport type { RootIndexCandidate } from \"../optimizer/genalgo.js\";\nimport type { ExportedStats } from \"../optimizer/statistics.js\";\nimport type { Nudge } from \"./nudges.js\";\nimport { ColumnReferencePart, TableMappings, Walker } from \"./walker.js\";\n\nexport interface DatabaseDriver {\n query(query: string, params: unknown[]): Promise<unknown[]>;\n}\n\nexport const ignoredIdentifier = \"__qd_placeholder\";\n\nexport interface SQLCommenterTag {\n key: string;\n value: string;\n}\n\nexport type SortContext = {\n dir: SortByDir;\n nulls: SortByNulls;\n};\n\nexport type DiscoveredColumnReference = {\n /** How often the column reference appears in the query. */\n frequency: number;\n /**\n * Representation of the column reference exactly\n * as it appears in the query.\n */\n representation: string;\n /**\n * Parts of the column reference separated by dots in the query.\n * The table reference (if it exists) is resolved if the query\n * uses an alias.\n *\n * Has 3 different potential configurations (in theory)\n * `a.b.c` - a column reference with a table and a schema reference\n * `a.b` - a column reference with a table reference but no schema\n * `a` - a column reference with no table reference.\n *\n * We use a simple array here to allow parsing of any syntactically correct\n * but logically incorrect query. The checks happen later when we're deriving\n * potential indexes from parts of a column reference in `deriveIndexes`\n */\n parts: ColumnReferencePart[];\n /**\n * Whether the column reference is invalid. This\n */\n ignored: boolean;\n /** The position of the column reference in the query. */\n position: {\n start: number;\n end: number;\n };\n /**\n * A sort direction associated by the column reference.\n * Only relevant to references from sorts\n */\n sort?: SortContext;\n where?: { nulltest?: NullTestType };\n};\n\n/** A function defined by @pgsql/parser */\nexport type Parser = (query: string) => Promise<unknown>;\n\nexport type TableReference = {\n schema?: string;\n table: string;\n};\n\nexport type AnalysisResult = {\n indexesToCheck: DiscoveredColumnReference[];\n ansiHighlightedQuery: string;\n referencedTables: TableReference[];\n shadowedAliases: ColumnReferencePart[];\n tags: SQLCommenterTag[];\n queryWithoutTags: string;\n formattedQueryWithoutTags?: string;\n nudges: Nudge[];\n};\n\nexport type SQLCommenterExtraction = {\n tags: SQLCommenterTag[];\n queryWithoutTags: string;\n};\n\n/**\n * Analyzes a query and returns a list of column references that\n * should be indexed.\n *\n * This should be instantiated once per analyzed query.\n */\nexport class Analyzer {\n constructor(private readonly parser: Parser) {}\n async analyze(\n query: string,\n formattedQuery?: string,\n ): Promise<AnalysisResult> {\n const ast = (await this.parser(query)) as ParseResult;\n if (!ast.stmts) {\n throw new Error(\n \"Query did not have any statements. This should probably never happen?\",\n );\n }\n const stmt = ast.stmts[0].stmt;\n if (!stmt) {\n throw new Error(\n \"Query did not have any statements. This should probably never happen?\",\n );\n }\n const walker = new Walker(query);\n const {\n highlights,\n indexRepresentations,\n indexesToCheck,\n shadowedAliases,\n tempTables,\n tableMappings,\n nudges,\n } = walker.walk(stmt);\n const sortedHighlights = highlights.sort(\n (a, b) => b.position.end - a.position.end,\n );\n let currQuery = query;\n for (const highlight of sortedHighlights) {\n // our parts might have\n const parts = this.resolveTableAliases(highlight.parts, tableMappings);\n if (parts.length === 0) {\n console.error(highlight);\n throw new Error(\"Highlight must have at least one part\");\n }\n let color: Color;\n let skip = false;\n if (highlight.ignored) {\n color = (x) => dim(strikethrough(x));\n skip = true;\n } else if (\n parts.length === 2 &&\n tempTables.has(parts[0].text) &&\n // sometimes temp tables are aliased as existing tables\n // we don't want to ignore them if they are\n !tableMappings.has(parts[0].text)\n ) {\n color = blue;\n skip = true;\n } else {\n color = bgMagentaBright;\n }\n const queryRepr = highlight.representation;\n const queryBeforeMatch = currQuery.slice(0, highlight.position.start);\n const queryAfterToken = currQuery.slice(highlight.position.end);\n currQuery = `${queryBeforeMatch}${color(queryRepr)}${this.colorizeKeywords(\n queryAfterToken,\n color,\n )}`;\n if (indexRepresentations.has(queryRepr)) {\n skip = true;\n }\n if (!skip) {\n indexesToCheck.push(highlight);\n indexRepresentations.add(queryRepr);\n }\n }\n\n const referencedTables: TableReference[] = [];\n for (const value of tableMappings.values()) {\n // aliased mappings are not concrete tables\n // eg: select * from table t -> t is not a table\n if (!value.alias) {\n referencedTables.push({\n schema: value.schema,\n table: value.text,\n });\n }\n }\n const { tags, queryWithoutTags } = this.extractSqlcommenter(query);\n\n const formattedQueryWithoutTags = formattedQuery\n ? this.extractSqlcommenter(formattedQuery).queryWithoutTags\n : undefined;\n\n return {\n indexesToCheck,\n ansiHighlightedQuery: currQuery,\n referencedTables,\n shadowedAliases,\n tags,\n queryWithoutTags,\n formattedQueryWithoutTags,\n nudges,\n };\n }\n\n deriveIndexes(\n tables: ExportedStats[],\n discovered: DiscoveredColumnReference[],\n referencedTables: TableReference[],\n ): RootIndexCandidate[] {\n /**\n * There are 3 different kinds of parts a col reference can have\n * {a} = just a column within context. Find out the table\n * {a, b} = a column reference with a table reference. There's still ambiguity here\n * with what the schema could be in case there are 2 tables with the same name in different schemas.\n * {a, b, c} = a column reference with a table reference and a schema reference.\n * This is the best case scenario.\n */\n const allIndexes: RootIndexCandidate[] = [];\n const seenIndexes = new Set<string>();\n function addIndex(index: RootIndexCandidate) {\n const key = `\"${index.schema}\":\"${index.table}\":\"${index.column}\"`;\n if (seenIndexes.has(key)) {\n return;\n }\n seenIndexes.add(key);\n allIndexes.push(index);\n }\n const matchingTables = this.filterReferences(referencedTables, tables);\n for (const colReference of discovered) {\n const partsCount = colReference.parts.length;\n const columnOnlyReference = partsCount === 1;\n const tableReference = partsCount === 2;\n const fullReference = partsCount === 3;\n if (columnOnlyReference) {\n // select c from x\n const [column] = colReference.parts;\n const referencedColumn = this.normalize(column);\n for (const table of matchingTables) {\n if (!this.hasColumn(table, referencedColumn)) {\n continue;\n }\n const index: RootIndexCandidate = {\n schema: table.schemaName,\n table: table.tableName,\n column: referencedColumn,\n };\n if (colReference.sort) {\n index.sort = colReference.sort;\n }\n if (colReference.where) {\n index.where = colReference.where;\n }\n addIndex(index);\n }\n } else if (tableReference) {\n // select b.c from x\n const [table, column] = colReference.parts;\n const referencedTable = this.normalize(table);\n const referencedColumn = this.normalize(column);\n for (const matchingTable of matchingTables) {\n if (!this.hasColumn(matchingTable, referencedColumn)) {\n continue;\n }\n const index: RootIndexCandidate = {\n schema: matchingTable.schemaName,\n table: referencedTable,\n column: referencedColumn,\n };\n if (colReference.sort) {\n index.sort = colReference.sort;\n }\n if (colReference.where) {\n index.where = colReference.where;\n }\n addIndex(index);\n }\n } else if (fullReference) {\n // select a.b.c from x\n const [schema, table, column] = colReference.parts;\n const referencedSchema = this.normalize(schema);\n const referencedTable = this.normalize(table);\n const referencedColumn = this.normalize(column);\n const index: RootIndexCandidate = {\n schema: referencedSchema,\n table: referencedTable,\n column: referencedColumn,\n };\n if (colReference.sort) {\n index.sort = colReference.sort;\n }\n if (colReference.where) {\n index.where = colReference.where;\n }\n addIndex(index);\n } else {\n // select huh.a.b.c from x\n console.error(\n \"Column reference has too many parts. The query is malformed\",\n colReference,\n );\n continue;\n }\n }\n return allIndexes;\n }\n\n private filterReferences(\n referencedTables: TableReference[],\n tables: ExportedStats[],\n ): ExportedStats[] {\n const matchingTables: ExportedStats[] = [];\n for (const referencedTable of referencedTables) {\n const refs = tables.filter(({ tableName, schemaName }) => {\n // not every referenced table carries a schema with it\n let schemaMatches = true;\n if (referencedTable.schema) {\n schemaMatches = schemaName === referencedTable.schema;\n }\n return schemaMatches && tableName === referencedTable.table;\n });\n matchingTables.push(...refs);\n }\n return matchingTables;\n }\n\n private hasColumn(table: ExportedStats, columnName: string): boolean {\n return (\n table.columns?.some((column) => column.columnName === columnName) ?? false\n );\n }\n\n private colorizeKeywords(query: string, color: Color) {\n return query\n .replace(\n // eh? This kinda sucks\n /(^\\s+)(asc|desc)?(\\s+(nulls first|nulls last))?/i,\n (_, pre, dir, spaceNulls, nulls) => {\n return `${pre}${dir ? color(dir) : \"\"}${\n nulls ? spaceNulls.replace(nulls, color(nulls)) : \"\"\n }`;\n },\n )\n .replace(/(^\\s+)(is (null|not null))/i, (_, pre, nulltest) => {\n return `${pre}${color(nulltest)}`;\n });\n }\n\n /**\n * Resolves aliases such as `a.b` to `x.b` if `a` is a known\n * alias to a table called x.\n *\n * Ignores all other combination of parts such as `a.b.c`\n */\n private resolveTableAliases(\n parts: ColumnReferencePart[],\n tableMappings: TableMappings,\n ): ColumnReferencePart[] {\n // we don't want to resolve aliases for references such as\n // `a.b.c` - this is fully qualified with a schema and can't be an alias\n // `c` - because there's no table reference here (as far as we can tell)\n if (parts.length !== 2) {\n return parts;\n }\n const tablePart = parts[0];\n const mapping = tableMappings.get(tablePart.text);\n if (mapping) {\n parts[0] = mapping;\n }\n return parts;\n }\n\n private normalize(columnReference: ColumnReferencePart): string {\n return columnReference.quoted\n ? columnReference.text\n : // postgres automatically lowercases column names if not quoted\n columnReference.text.toLowerCase();\n }\n\n private extractSqlcommenter(query: string): SQLCommenterExtraction {\n const trimmedQuery = query.trimEnd();\n const startPosition = trimmedQuery.lastIndexOf(\"/*\");\n const endPosition = trimmedQuery.lastIndexOf(\"*/\");\n if (startPosition === -1 || endPosition === -1) {\n return { tags: [], queryWithoutTags: trimmedQuery };\n }\n const queryWithoutTags = trimmedQuery.slice(0, startPosition);\n const tagString = trimmedQuery.slice(startPosition + 2, endPosition).trim();\n if (!tagString || typeof tagString !== \"string\") {\n return { tags: [], queryWithoutTags: queryWithoutTags };\n }\n const tags: SQLCommenterTag[] = [];\n for (const match of tagString.split(\",\")) {\n const [key, value] = match.split(\"=\");\n // just because a comment has a `,` but not a `=` in it doesn't\n // mean that it's a malformed sqlcommenter tag. It might just be\n // a long comment with good punctuation.\n if (!key || !value) {\n // however, if there was a previously valid tag, the comment\n // is more likely to be a malformed sqlcommenter tag\n if (tags.length > 0) {\n console.warn(\n `Invalid sqlcommenter tag: ${match} in comment: ${tagString}. Ignoring`,\n );\n }\n continue;\n }\n try {\n let sliceStart = 0;\n if (value.startsWith(\"'\")) {\n sliceStart = 1;\n }\n let sliceEnd = value.length;\n if (value.endsWith(\"'\")) {\n sliceEnd -= 1;\n }\n\n const decoded = decodeURIComponent(value.slice(sliceStart, sliceEnd));\n // should we be trimming here?\n tags.push({ key: key.trim(), value: decoded });\n } catch (err) {\n // we want to be very conservative with this parser and ignore errors\n console.error(err);\n }\n }\n return { tags, queryWithoutTags };\n }\n}\n","import type { Node, NullTestType } from \"@pgsql/types\";\nimport { deparseSync } from \"pgsql-deparser\";\nimport type { DiscoveredColumnReference, SortContext } from \"./analyzer.js\";\nimport { parseNudges } from \"./nudges.js\";\nimport type { Nudge } from \"./nudges.js\";\n\n/** Information about tables that appear in the query */\nexport type TableMappings = Map<string, ColumnReferencePart>;\ntype SeenReferences = Map<string, number>;\n\n/**\n * Walks the AST of a sql query and extracts query metadata.\n * This pattern is used to segregate the mutable state that's more common for the\n * AST walking process from the rest of the analyzer.\n */\nexport class Walker {\n private tableMappings: TableMappings = new Map();\n private tempTables = new Set<string>();\n private highlights: DiscoveredColumnReference[] = [];\n private indexRepresentations = new Set<string>();\n private indexesToCheck: DiscoveredColumnReference[] = [];\n private highlightPositions = new Set<number>();\n // used for tallying the amount of times we see stuff so\n // we have a better idea of what to start off the algorithm with\n private seenReferences: SeenReferences = new Map();\n private shadowedAliases: ColumnReferencePart[] = [];\n private nudges: Nudge[] = [];\n\n constructor(private readonly query: string) {}\n\n walk(root: Node) {\n // reset state in case the class instance is reused\n // reassigning vars here instead of using `map.clear()` to prevent\n // accidentally mutating existing references\n this.tableMappings = new Map();\n this.tempTables = new Set<string>();\n this.highlights = [];\n this.indexRepresentations = new Set<string>();\n this.indexesToCheck = [];\n this.highlightPositions = new Set<number>();\n this.seenReferences = new Map();\n this.shadowedAliases = [];\n this.nudges = [];\n\n Walker.traverse(root, [], (node, stack) => {\n const nodeNudges = parseNudges(node, stack);\n this.nudges = [...this.nudges, ...nodeNudges];\n\n // comments are not parsed here as they seem to be ignored.\n //\n // results cannot be indexed in any way because they alias a CTE\n // with alias as (select ...)\n // ^^^^^\n if (is(node, \"CommonTableExpr\")) {\n if (node.CommonTableExpr.ctename) {\n this.tempTables.add(node.CommonTableExpr.ctename);\n }\n }\n // results cannot be indexed in any way because they alias a subquery\n // select ... from (...) as alias\n // ^^^^^\n if (is(node, \"RangeSubselect\")) {\n if (node.RangeSubselect.alias?.aliasname) {\n this.tempTables.add(node.RangeSubselect.alias.aliasname);\n }\n }\n // select ... from (...) where col is null\n // ^^^^^^^\n if (is(node, \"NullTest\")) {\n if (\n node.NullTest.arg &&\n node.NullTest.nulltesttype &&\n is(node.NullTest.arg, \"ColumnRef\")\n ) {\n this.add(node.NullTest.arg, {\n where: { nulltest: node.NullTest.nulltesttype },\n });\n }\n }\n // can be indexed as the alias refers to a regular table\n // but the alias has to be mapped to the original table name\n // select ... from table as alias\n // ^^^^^\n if (is(node, \"RangeVar\") && node.RangeVar.relname) {\n const columnReference: ColumnReferencePart = {\n text: node.RangeVar.relname,\n start: node.RangeVar.location,\n quoted: false,\n };\n if (node.RangeVar.schemaname) {\n columnReference.schema = node.RangeVar.schemaname;\n }\n this.tableMappings.set(node.RangeVar.relname, columnReference);\n // In theory we can't blindly map aliases to table names\n // it's possible that two aliases point to different tables\n // which postgres allows but is tricky to determine by just walking\n // the AST like we're doing currently.\n if (node.RangeVar.alias?.aliasname) {\n const aliasName = node.RangeVar.alias.aliasname;\n const existingMapping = this.tableMappings.get(aliasName);\n const part: ColumnReferencePart = {\n text: node.RangeVar.relname,\n start: node.RangeVar.location,\n // what goes here? the text here doesn't _really_ exist.\n // so it can't be quoted or not quoted.\n // Does it even matter?\n quoted: true,\n alias: aliasName,\n };\n if (node.RangeVar.schemaname) {\n part.schema = node.RangeVar.schemaname;\n }\n // Postgres supports shadowing table aliases created in different levels of queries\n // for example:\n // ```\n // SELECT t.id, t.name\n // FROM users t\n // WHERE EXISTS (\n // SELECT 1\n // FROM orders t -- shadows outer alias \"t\"\n // WHERE t.user_id = users.id\n // );\n // ```\n // but we're very unlikely to see this in practice. Every ORM I've seen so far\n // has produced globally unique aliases. This is not worth the complexity currently.\n // but is almost certainly guaranteed to be a problem in the future.\n if (existingMapping) {\n const isSystemCatalog =\n node.RangeVar.relname?.startsWith(\"pg_\") ?? false;\n if (!isSystemCatalog) {\n console.warn(\n `Ignoring alias ${aliasName} as it shadows an existing mapping for ${existingMapping.text}. We currently do not support alias shadowing.`,\n );\n }\n // Let the user know what happened but don't stop the show.\n this.shadowedAliases.push(part);\n return;\n }\n this.tableMappings.set(aliasName, part);\n }\n }\n // select ... from table order by col asc\n // ^^^^^^^^^^^^^^^^\n if (is(node, \"SortBy\")) {\n // we don't care about sorting by anything that's not a column reference\n // because it couldn't be indexed anyway.\n // TODO: mark that expression as unindexable? It's just better for debugging\n if (node.SortBy.node && is(node.SortBy.node, \"ColumnRef\")) {\n this.add(node.SortBy.node, {\n sort: {\n dir: node.SortBy.sortby_dir ?? \"SORTBY_DEFAULT\",\n nulls: node.SortBy.sortby_nulls ?? \"SORTBY_NULLS_DEFAULT\",\n },\n });\n }\n }\n // select ... from table1 join table2 t2 on table1.col = t2.col\n // ^^\n if (is(node, \"JoinExpr\") && node.JoinExpr.quals) {\n if (is(node.JoinExpr.quals, \"A_Expr\")) {\n if (\n node.JoinExpr.quals.A_Expr.lexpr &&\n is(node.JoinExpr.quals.A_Expr.lexpr, \"ColumnRef\")\n ) {\n this.add(node.JoinExpr.quals.A_Expr.lexpr);\n }\n if (\n node.JoinExpr.quals.A_Expr.rexpr &&\n is(node.JoinExpr.quals.A_Expr.rexpr, \"ColumnRef\")\n ) {\n this.add(node.JoinExpr.quals.A_Expr.rexpr);\n }\n }\n }\n // any column reference anywhere\n if (is(node, \"ColumnRef\")) {\n // TODO: this approach needs refinement\n for (let i = 0; i < stack.length; i++) {\n const inReturningList =\n stack[i] === \"returningList\" &&\n stack[i + 1] === \"ResTarget\" &&\n stack[i + 2] === \"val\" &&\n stack[i + 3] === \"ColumnRef\";\n if (inReturningList) {\n this.add(node, { ignored: true });\n return;\n }\n if (\n // stack[i] === \"SelectStmt\" &&\n stack[i + 1] === \"targetList\" &&\n stack[i + 2] === \"ResTarget\" &&\n stack[i + 3] === \"val\" &&\n stack[i + 4] === \"ColumnRef\"\n ) {\n // we don't want to index the columns that are being selected\n this.add(node, { ignored: true });\n return;\n }\n\n // TODO: add functional index support here\n if (stack[i] === \"FuncCall\" && stack[i + 1] === \"args\") {\n // args of a function call can't be indexed (without functional indexes)\n this.add(node, { ignored: true });\n return;\n }\n }\n this.add(node);\n }\n });\n\n return {\n highlights: this.highlights,\n indexRepresentations: this.indexRepresentations,\n indexesToCheck: this.indexesToCheck,\n shadowedAliases: this.shadowedAliases,\n tempTables: this.tempTables,\n tableMappings: this.tableMappings,\n nudges: this.nudges,\n };\n }\n\n private add(\n node: Extract<Node, { ColumnRef: unknown }>,\n options?: {\n ignored?: boolean;\n sort?: SortContext;\n where?: { nulltest?: NullTestType };\n },\n ) {\n if (!node.ColumnRef.location) {\n console.error(`Node did not have a location. Skipping`, node);\n return;\n }\n if (!node.ColumnRef.fields) {\n console.error(node);\n throw new Error(\"Column reference must have fields\");\n }\n let ignored = options?.ignored ?? false;\n let runningLength: number = node.ColumnRef.location;\n const parts: ColumnReferencePart[] = node.ColumnRef.fields.map(\n (field, i, length) => {\n if (!is(field, \"String\") || !field.String.sval) {\n const out = deparseSync(field);\n ignored = true;\n return {\n quoted: out.startsWith('\"'),\n text: out,\n start: runningLength,\n };\n }\n const start = runningLength;\n const size = field.String.sval?.length ?? 0;\n let quoted = false;\n if (node.ColumnRef.location !== undefined) {\n const boundary = this.query[runningLength];\n if (boundary === '\"') {\n quoted = true;\n }\n }\n // +1 for the dot that comes after\n const isLastIteration = i === length.length - 1;\n runningLength += size + (isLastIteration ? 0 : 1) + (quoted ? 2 : 0);\n return {\n text: field.String.sval,\n start,\n quoted,\n };\n },\n );\n const end = runningLength;\n if (this.highlightPositions.has(node.ColumnRef.location)) {\n return;\n }\n this.highlightPositions.add(node.ColumnRef.location);\n const highlighted = `${this.query.slice(node.ColumnRef.location, end)}`;\n const seen = this.seenReferences.get(highlighted);\n if (!ignored) {\n this.seenReferences.set(highlighted, (seen ?? 0) + 1);\n }\n const ref: DiscoveredColumnReference = {\n frequency: seen ?? 1,\n representation: highlighted,\n parts,\n ignored: ignored ?? false,\n position: {\n start: node.ColumnRef.location,\n end,\n },\n };\n if (options?.sort) {\n ref.sort = options.sort;\n }\n if (options?.where) {\n ref.where = options.where;\n }\n this.highlights.push(ref);\n }\n\n static traverse(\n node: unknown,\n stack: (KeysOfUnion<Node> | string)[],\n callback: (node: Node, stack: (KeysOfUnion<Node> | string)[]) => void,\n ) {\n if (isANode(node)) {\n callback(node, [...stack, getNodeKind(node)]);\n }\n if (typeof node !== \"object\" || node === null) {\n return;\n }\n if (Array.isArray(node)) {\n for (const item of node) {\n if (isANode(item)) {\n Walker.traverse(item, stack, callback);\n }\n }\n } else if (isANode(node)) {\n const keys = Object.keys(node);\n // @ts-expect-error | nodes don't allow dynamic access but it's the only way to do it\n Walker.traverse(node[keys[0]], [...stack, getNodeKind(node)], callback);\n } else {\n for (const [key, child] of Object.entries(node)) {\n Walker.traverse(child, [...stack, key as KeysOfUnion<Node>], callback);\n }\n }\n }\n}\n\nexport type ColumnReferencePart = {\n schema?: string;\n /** the text of the column reference (excluding any potential quotes) */\n text: string;\n start?: number;\n quoted: boolean;\n alias?: string;\n};\n\ntype KeysOfUnion<T> = T extends T ? keyof T : never;\nfunction is<K extends KeysOfUnion<Node>>(\n node: Node,\n kind: K,\n): node is Extract<Node, Record<K, unknown>> {\n return kind in node;\n}\n\nfunction getNodeKind(node: Node): KeysOfUnion<Node> {\n const keys = Object.keys(node);\n return keys[0] as KeysOfUnion<Node>;\n}\n\nfunction isANode(node: unknown): node is Node {\n if (typeof node !== \"object\" || node === null) {\n return false;\n }\n const keys = Object.keys(node);\n return keys.length === 1 && /^[A-Z]/.test(keys[0]);\n}\n","import type { List, Node } from \"@pgsql/types\";\n\ntype NudgeKind =\n | \"LARGE_IMPROVEMENT_FOUND\"\n | \"SMALL_IMPROVEMENT_FOUND\"\n | \"AVOID_SELECT_STAR\"\n | \"AVOID_FUNCTIONS_ON_COLUMNS_IN_WHERE\"\n | \"MISSING_WHERE_CLAUSE\"\n | \"MISSING_LIMIT_CLAUSE\"\n | \"USE_IS_NULL_NOT_EQUALS\"\n | \"AVOID_DISTINCT_WITHOUT_REASON\"\n | \"MISSING_JOIN_CONDITION\"\n | \"AVOID_LEADING_WILDCARD_LIKE\"\n | \"CONSIDER_IN_INSTEAD_OF_MANY_ORS\"\n | \"REPLACE_LARGE_IN_TUPLE_WITH_ANY_ARRAY\";\n\nexport type Nudge = {\n kind: NudgeKind;\n severity: \"CRITICAL\" | \"WARNING\" | \"INFO\";\n message: string;\n};\n\ntype KeysOfUnion<T> = T extends T ? keyof T : never;\n\nfunction is<K extends KeysOfUnion<Node>>(\n node: Node,\n kind: K,\n): node is Extract<Node, Record<K, unknown>> {\n return kind in node;\n}\n\nfunction isANode(node: unknown): node is Node {\n if (typeof node !== \"object\" || node === null) {\n return false;\n }\n const keys = Object.keys(node);\n return keys.length === 1 && /^[A-Z]/.test(keys[0]);\n}\n\n/**\n * Detect nudges for a single node during AST traversal.\n * Returns an array of nudges found for this node.\n */\nexport function parseNudges(\n node: Node,\n stack: (KeysOfUnion<Node> | string)[],\n): Nudge[] {\n const nudges: Nudge[] = [];\n\n if (is(node, \"A_Star\")) {\n nudges.push({\n kind: \"AVOID_SELECT_STAR\",\n severity: \"INFO\",\n message: \"Avoid using SELECT *\",\n });\n }\n\n // Detect functions on columns in WHERE clause\n if (is(node, \"FuncCall\")) {\n // Check if we're in a WHERE clause context\n const inWhereClause = stack.some((item) => item === \"whereClause\");\n if (inWhereClause && node.FuncCall.args) {\n // Check if any function arguments contain column references\n const hasColumnRef = containsColumnRef(node.FuncCall.args);\n if (hasColumnRef) {\n nudges.push({\n kind: \"AVOID_FUNCTIONS_ON_COLUMNS_IN_WHERE\",\n severity: \"WARNING\",\n message: \"Avoid using functions on columns in WHERE clause\",\n });\n }\n }\n }\n\n // Detect unbounded queries (missing WHERE or LIMIT on table queries)\n if (is(node, \"SelectStmt\")) {\n // Only check top-level SELECT statements (not subqueries)\n const isSubquery = stack.some(\n (item) =>\n item === \"RangeSubselect\" ||\n item === \"SubLink\" ||\n item === \"CommonTableExpr\",\n );\n\n if (!isSubquery) {\n const hasFromClause =\n node.SelectStmt.fromClause && node.SelectStmt.fromClause.length > 0;\n if (hasFromClause) {\n // Check if this SELECT queries actual tables (not just subqueries or CTEs)\n const hasActualTables = node.SelectStmt.fromClause!.some((fromItem) => {\n return (\n is(fromItem, \"RangeVar\") ||\n (is(fromItem, \"JoinExpr\") && hasActualTablesInJoin(fromItem))\n );\n });\n\n if (hasActualTables) {\n // Check for missing WHERE clause\n if (!node.SelectStmt.whereClause) {\n nudges.push({\n kind: \"MISSING_WHERE_CLAUSE\",\n severity: \"INFO\",\n message: \"Missing WHERE clause\",\n });\n }\n\n // Check for missing LIMIT clause\n if (!node.SelectStmt.limitCount) {\n nudges.push({\n kind: \"MISSING_LIMIT_CLAUSE\",\n severity: \"INFO\",\n message: \"Missing LIMIT clause\",\n });\n }\n }\n }\n }\n }\n\n // Detect NULL comparison issues (= NULL instead of IS NULL)\n if (is(node, \"A_Expr\")) {\n const isEqualityOp =\n node.A_Expr.kind === \"AEXPR_OP\" &&\n node.A_Expr.name &&\n node.A_Expr.name.length > 0 &&\n is(node.A_Expr.name[0], \"String\") &&\n (node.A_Expr.name[0].String.sval === \"=\" ||\n node.A_Expr.name[0].String.sval === \"!=\" ||\n node.A_Expr.name[0].String.sval === \"<>\");\n\n if (isEqualityOp) {\n const leftIsNull = isNullConstant(node.A_Expr.lexpr);\n const rightIsNull = isNullConstant(node.A_Expr.rexpr);\n\n if (leftIsNull || rightIsNull) {\n nudges.push({\n kind: \"USE_IS_NULL_NOT_EQUALS\",\n severity: \"WARNING\",\n message: \"Use IS NULL instead of = or != or <> for NULL comparisons\",\n });\n }\n }\n\n // Detect LIKE with leading wildcards\n const isLikeOp =\n node.A_Expr.kind === \"AEXPR_LIKE\" || node.A_Expr.kind === \"AEXPR_ILIKE\";\n\n if (isLikeOp && node.A_Expr.rexpr) {\n const patternString = getStringConstantValue(node.A_Expr.rexpr);\n if (patternString && patternString.startsWith(\"%\")) {\n nudges.push({\n kind: \"AVOID_LEADING_WILDCARD_LIKE\",\n severity: \"WARNING\",\n message: \"Avoid using LIKE with leading wildcards\",\n });\n }\n }\n }\n\n // Detect DISTINCT usage\n if (is(node, \"SelectStmt\") && node.SelectStmt.distinctClause) {\n nudges.push({\n kind: \"AVOID_DISTINCT_WITHOUT_REASON\",\n severity: \"WARNING\",\n message: \"Avoid using DISTINCT without a reason\",\n });\n }\n\n // Detect cartesian joins (missing JOIN conditions)\n if (is(node, \"JoinExpr\")) {\n // Check if JOIN has no qualification (ON clause)\n if (!node.JoinExpr.quals) {\n nudges.push({\n kind: \"MISSING_JOIN_CONDITION\",\n severity: \"WARNING\",\n message: \"Missing JOIN condition\",\n });\n }\n }\n\n // Detect multiple tables in FROM without explicit JOINs (old-style cartesian joins)\n if (\n is(node, \"SelectStmt\") &&\n node.SelectStmt.fromClause &&\n node.SelectStmt.fromClause.length > 1\n ) {\n // Check if there are multiple RangeVar (tables) directly in FROM clause\n const tableCount = node.SelectStmt.fromClause.filter((item) =>\n is(item, \"RangeVar\"),\n ).length;\n if (tableCount > 1) {\n nudges.push({\n kind: \"MISSING_JOIN_CONDITION\",\n severity: \"WARNING\",\n message: \"Missing JOIN condition\",\n });\n }\n }\n\n // Detect too many OR conditions\n if (is(node, \"BoolExpr\") && node.BoolExpr.boolop === \"OR_EXPR\") {\n const orCount = countBoolOrConditions(node);\n if (orCount >= 3) {\n nudges.push({\n kind: \"CONSIDER_IN_INSTEAD_OF_MANY_ORS\",\n severity: \"WARNING\",\n message: \"Consider using IN instead of many ORs\",\n });\n }\n }\n\n // Too many parameters in a tuple\n if (is(node, \"A_Expr\")) {\n if (node.A_Expr.kind === \"AEXPR_IN\") {\n let list: List | undefined;\n if (node.A_Expr.lexpr && is(node.A_Expr.lexpr, \"List\")) {\n list = node.A_Expr.lexpr.List;\n } else if (node.A_Expr.rexpr && is(node.A_Expr.rexpr, \"List\")) {\n list = node.A_Expr.rexpr.List;\n }\n\n if (list?.items && list.items.length >= 10) {\n nudges.push({\n kind: \"REPLACE_LARGE_IN_TUPLE_WITH_ANY_ARRAY\",\n message:\n \"`in (...)` queries with large tuples can often be replaced with `= ANY($1)` using a single parameter\",\n severity: \"INFO\",\n });\n }\n }\n }\n\n return nudges;\n}\n\nfunction containsColumnRef(args: unknown[]): boolean {\n // Recursively check if any argument contains a ColumnRef\n for (const arg of args) {\n if (hasColumnRefInNode(arg)) {\n return true;\n }\n }\n return false;\n}\n\nfunction hasColumnRefInNode(node: unknown): boolean {\n if (isANode(node) && is(node, \"ColumnRef\")) {\n return true;\n }\n\n if (typeof node !== \"object\" || node === null) {\n return false;\n }\n\n if (Array.isArray(node)) {\n return node.some((item) => hasColumnRefInNode(item));\n }\n\n if (isANode(node)) {\n const keys = Object.keys(node);\n // @ts-expect-error | nodes don't allow dynamic access but it's the only way to do it\n return hasColumnRefInNode(node[keys[0]]);\n }\n\n for (const child of Object.values(node)) {\n if (hasColumnRefInNode(child)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction hasActualTablesInJoin(\n joinExpr: Extract<Node, { JoinExpr: unknown }>,\n): boolean {\n // Check left side of join\n if (joinExpr.JoinExpr.larg && is(joinExpr.JoinExpr.larg, \"RangeVar\")) {\n return true;\n }\n if (joinExpr.JoinExpr.larg && is(joinExpr.JoinExpr.larg, \"JoinExpr\")) {\n if (hasActualTablesInJoin(joinExpr.JoinExpr.larg)) {\n return true;\n }\n }\n\n // Check right side of join\n if (joinExpr.JoinExpr.rarg && is(joinExpr.JoinExpr.rarg, \"RangeVar\")) {\n return true;\n }\n if (joinExpr.JoinExpr.rarg && is(joinExpr.JoinExpr.rarg, \"JoinExpr\")) {\n if (hasActualTablesInJoin(joinExpr.JoinExpr.rarg)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction isNullConstant(node: unknown): boolean {\n if (!node || typeof node !== \"object\") {\n return false;\n }\n\n if (isANode(node) && is(node, \"A_Const\")) {\n // Check if it's a NULL constant\n return node.A_Const.isnull !== undefined;\n }\n\n return false;\n}\n\nfunction getStringConstantValue(node: unknown): string | null {\n if (!node || typeof node !== \"object\") {\n return null;\n }\n\n if (isANode(node) && is(node, \"A_Const\") && node.A_Const.sval) {\n return node.A_Const.sval.sval || null;\n }\n\n return null;\n}\n\nfunction countBoolOrConditions(\n node: Extract<Node, { BoolExpr: unknown }>,\n): number {\n if (node.BoolExpr.boolop !== \"OR_EXPR\" || !node.BoolExpr.args) {\n return 1;\n }\n\n let count = 0;\n for (const arg of node.BoolExpr.args) {\n if (\n isANode(arg) &&\n is(arg, \"BoolExpr\") &&\n arg.BoolExpr.boolop === \"OR_EXPR\"\n ) {\n count += countBoolOrConditions(arg);\n } else {\n count += 1;\n }\n }\n\n return count;\n}\n","import { z } from \"zod\";\nimport type { PgIdentifier } from \"./pg-identifier\";\n\nexport const PostgresVersion = z.string().brand(\"PostgresVersion\");\nexport type PostgresVersion = z.infer<typeof PostgresVersion>;\n\nexport interface PostgresTransaction {\n /**\n * Exec a query and return the result as an array of objects.\n */\n exec<T>(query: string, params?: unknown[]): Promise<T[]>;\n}\n\n/**\n * A shared interface for all postgres connections.\n * This is required to allow interop between pglite and regular postgres drivers\n */\nexport interface Postgres extends PostgresTransaction {\n transaction<T>(callback: (tx: PostgresTransaction) => Promise<T>): Promise<T>;\n cursor?<T>(\n query: string,\n params?: unknown[],\n options?: { size?: number },\n ): AsyncGenerator<T, void, unknown>;\n // postgres returns versions as a string\n serverNum(): Promise<PostgresVersion>;\n}\n\nexport type PostgresConnectionInput = {\n url: string;\n};\n\nexport type PostgresFactory = (input: PostgresConnectionInput) => Postgres;\n\n// PostgreSQL EXPLAIN plan types\ndeclare const StageId: unique symbol;\nexport type PostgresStageId = number & { [StageId]: \"StageId\" };\n\nexport type PostgresStage =\n | \"Seq Scan\"\n | \"Limit\"\n | \"Bitmap Heap Scan\"\n | \"Bitmap Index Scan\"\n | \"Index Only Scan\"\n | \"Index Scan\"\n | \"BitmapOr\";\n\nexport type PostgresExplainStageCommon = {\n \"Node Type\": PostgresStage;\n // available only when doing a trace explain using the\n // query doctor postgres binary\n \"Node Id\": PostgresStageId;\n Plans?: PostgresExplainStage[];\n \"Plan Width\": number;\n \"Total Cost\": number;\n};\n\nexport type PostgresExplainStage =\n | (PostgresExplainStageCommon & {\n \"Node Type\": \"Index Scan\";\n \"Index Name\": string;\n \"Relation Name\": string;\n Alias: string;\n \"Rows Removed by Filter\": number;\n })\n | (PostgresExplainStageCommon & {\n \"Node Type\": \"Seq Scan\";\n \"Relation Name\": string;\n Filter?: string;\n \"Actual Rows\": number;\n \"Actual Loops\": number;\n \"Rows Removed by Filter\": number;\n })\n | (PostgresExplainStageCommon & {\n \"Node Type\": \"Bitmap Heap Scan\";\n \"Relation Name\": string;\n Filter?: string;\n \"Actual Rows\": number;\n \"Actual Loops\": number;\n \"Rows Removed by Filter\": number;\n })\n | (PostgresExplainStageCommon & {\n \"Node Type\": \"Bitmap Index Scan\";\n \"Index Name\": string;\n Filter?: string;\n \"Actual Rows\": number;\n \"Actual Loops\": number;\n \"Rows Removed by Filter\": number;\n })\n | (PostgresExplainStageCommon & {\n \"Node Type\": \"Index Only Scan\";\n \"Index Name\": string;\n Filter?: string;\n \"Actual Rows\": number;\n \"Actual Loops\": number;\n \"Rows Removed by Filter\": number;\n })\n | PostgresExplainStageCommon;\n\nexport type PostgresExplainResult = {\n \"QUERY PLAN\": {\n Plan: PostgresExplainStage;\n }[];\n};\n\n/**\n * Drops a disabled index. Rollsback if it fails for any reason\n * @returns Did dropping the index succeed?\n */\nexport async function dropIndex(\n tx: PostgresTransaction,\n index: PgIdentifier,\n): Promise<boolean> {\n try {\n await tx.exec(`\n savepoint idx_drop;\n drop index if exists ${index} cascade;\n `);\n return true;\n } catch (error) {\n // no problem if droping the index fails. It should throw an error\n await tx.exec(`rollback to idx_drop`);\n return false;\n }\n}\n","import type { IndexedTable } from \"../optimizer/statistics.js\";\n\nexport function isIndexSupported(index: IndexedTable) {\n return index.index_type === \"btree\";\n}\n\n/**\n * Doesn't necessarily decide whether the index can be dropped but can be\n * used to not even try dropping indexes that _definitely_ cannot be dropped\n */\nexport function isIndexProbablyDroppable(index: IndexedTable) {\n /* TODO: until we have a better solution, this is the best we have */\n /* The is_unique check is problematic only if the column is declared as unique */\n return !index.is_primary && !index.is_unique;\n}\n","import { PgIdentifier } from \"./pg-identifier\";\n\nexport type PostgresQueryBuilderCommand =\n | \"bitmapscan\"\n | \"indexscan\"\n | \"seqscan\";\n\nexport class PostgresQueryBuilder {\n private readonly commands: Record<string, string> = {};\n private isIntrospection: boolean = false;\n private explainFlags: string[] = [];\n private _preamble = 0;\n\n constructor(private query: string) {}\n\n get preamble(): number {\n return this._preamble;\n }\n\n static createIndex(definition: string, name?: PgIdentifier) {\n if (name) {\n return new PostgresQueryBuilder(\n `create index \"${name}\" on ${definition};`,\n );\n }\n return new PostgresQueryBuilder(`create index on ${definition};`);\n }\n\n enable(command: PostgresQueryBuilderCommand, value: boolean = true): this {\n const commandString = `enable_${command}`;\n if (value) {\n this.commands[commandString] = \"on\";\n } else {\n this.commands[commandString] = \"off\";\n }\n return this;\n }\n\n withQuery(query: string): this {\n this.query = query;\n return this;\n }\n\n introspect(): this {\n this.isIntrospection = true;\n return this;\n }\n\n explain(flags: string[]): this {\n this.explainFlags = flags;\n return this;\n }\n\n build(): string {\n let commands = this.generateSetCommands();\n commands += this.generateExplain().query;\n if (this.isIntrospection) {\n commands += \" -- @qd_introspection\";\n }\n return commands;\n }\n\n /** Return the \"set a=b\" parts of the command in the query separate from the explain select ... part */\n buildParts() {\n const commands = this.generateSetCommands();\n const explain = this.generateExplain();\n this._preamble = explain.preamble;\n if (this.isIntrospection) {\n explain.query += \" -- @qd_introspection\";\n }\n return { commands, query: explain.query };\n }\n\n private generateSetCommands() {\n let commands = \"\";\n for (const key in this.commands) {\n const value = this.commands[key];\n commands += `set local ${key}=${value};\\n`;\n }\n return commands;\n }\n\n private generateExplain() {\n let query = \"\";\n if (this.explainFlags.length > 0) {\n query += `explain (${this.explainFlags.join(\", \")}) `;\n }\n const semicolon = this.query.endsWith(\";\") ? \"\" : \";\";\n\n const preamble = query.length;\n query += `${this.query}${semicolon}`;\n return { query, preamble };\n }\n}\n","/**\n * Represents an identifier in postgres that is subject\n * to quoting rules. The {@link toString} rule behaves\n * exactly like calling `select quote_ident($1)` in postgres\n */\nexport class PgIdentifier {\n private constructor(\n private readonly value: string,\n private readonly quoted: boolean,\n ) {}\n\n /**\n * Constructs an identifier from a single part (column or table name).\n * When quoting identifiers like `select table.col` use {@link fromParts} instead\n */\n static fromString(identifier: string): PgIdentifier {\n const identifierRegex = /^[a-z_][a-zA-Z0-9_]*$/;\n const match = identifier.match(/^\"(.+)\"$/);\n if (match) {\n return new PgIdentifier(match[1], true);\n }\n const quoted =\n !identifierRegex.test(identifier) ||\n this.reservedKeywords.has(identifier.toLowerCase());\n return new PgIdentifier(identifier, quoted);\n }\n\n /**\n * Quotes parts of an identifier like `select schema.table.col`.\n * A separate function is necessary because postgres will treat\n * `select \"HELLO.WORLD\"` as a column name. It has to be like\n * `select \"HELLO\".\"WORLD\"` instead.\n */\n static fromParts(...identifiers: (string | PgIdentifier)[]): PgIdentifier {\n return new PgIdentifier(\n identifiers\n .map((identifier) => {\n if (typeof identifier === \"string\") {\n return PgIdentifier.fromString(identifier);\n } else {\n return identifier;\n }\n })\n .join(\".\"),\n false,\n );\n }\n\n toString(): string {\n if (this.quoted) {\n return `\"${this.value.replace(/\"/g, '\"\"')}\"`;\n }\n return this.value;\n }\n\n toJSON(): string {\n return this.toString();\n }\n\n // Every keyword that's not explicitly marked as\n // unreserved in src/include/parser/kwlist.h\n private static readonly reservedKeywords = new Set([\n \"all\",\n \"analyse\",\n \"analyze\",\n \"and\",\n \"any\",\n \"array\",\n \"as\",\n \"asc\",\n \"asymmetric\",\n \"authorization\",\n \"between\",\n \"bigint\",\n \"binary\",\n \"bit\",\n \"boolean\",\n \"both\",\n \"case\",\n \"cast\",\n \"char\",\n \"character\",\n \"check\",\n \"coalesce\",\n \"collate\",\n \"collation\",\n \"column\",\n \"concurrently\",\n \"constraint\",\n \"create\",\n \"cross\",\n \"current_catalog\",\n \"current_date\",\n \"current_role\",\n \"current_schema\",\n \"current_time\",\n \"current_timestamp\",\n \"current_user\",\n \"dec\",\n \"decimal\",\n \"default\",\n \"deferrable\",\n \"desc\",\n \"distinct\",\n \"do\",\n \"else\",\n \"end\",\n \"except\",\n \"exists\",\n \"extract\",\n \"false\",\n \"fetch\",\n \"float\",\n \"for\",\n \"foreign\",\n \"freeze\",\n \"from\",\n \"full\",\n \"grant\",\n \"greatest\",\n \"group\",\n \"grouping\",\n \"having\",\n \"ilike\",\n \"in\",\n \"initially\",\n \"inner\",\n \"inout\",\n \"int\",\n \"integer\",\n \"intersect\",\n \"interval\",\n \"into\",\n \"is\",\n \"isnull\",\n \"join\",\n \"json\",\n \"json_array\",\n \"json_arrayagg\",\n \"json_exists\",\n \"json_object\",\n \"json_objectagg\",\n \"json_query\",\n \"json_scalar\",\n \"json_serialize\",\n \"json_table\",\n \"json_value\",\n \"lateral\",\n \"leading\",\n \"least\",\n \"left\",\n \"like\",\n \"limit\",\n \"localtime\",\n \"localtimestamp\",\n \"merge_action\",\n \"national\",\n \"natural\",\n \"nchar\",\n \"none\",\n \"normalize\",\n \"not\",\n \"notnull\",\n \"null\",\n \"nullif\",\n \"numeric\",\n \"offset\",\n \"on\",\n \"only\",\n \"or\",\n \"order\",\n \"out\",\n \"outer\",\n \"overlaps\",\n \"overlay\",\n \"placing\",\n \"position\",\n \"precision\",\n \"primary\",\n \"real\",\n \"references\",\n \"returning\",\n \"right\",\n \"row\",\n \"select\",\n \"session_user\",\n \"setof\",\n \"similar\",\n \"smallint\",\n \"some\",\n \"substring\",\n \"symmetric\",\n \"system_user\",\n \"table\",\n \"tablesample\",\n \"then\",\n \"time\",\n \"timestamp\",\n \"to\",\n \"trailing\",\n \"treat\",\n \"trim\",\n \"true\",\n \"union\",\n \"unique\",\n \"user\",\n \"using\",\n \"values\",\n \"varchar\",\n \"variadic\",\n \"verbose\",\n \"when\",\n \"where\",\n \"window\",\n \"with\",\n \"xmlattributes\",\n \"xmlconcat\",\n \"xmlelement\",\n \"xmlexists\",\n \"xmlforest\",\n \"xmlnamespaces\",\n \"xmlparse\",\n \"xmlpi\",\n \"xmlroot\",\n \"xmlserialize\",\n \"xmltable\",\n ]);\n}\n","import type { NullTestType } from \"@pgsql/types\";\nimport { blue, gray, green, magenta, red, yellow } from \"colorette\";\nimport { isIndexProbablyDroppable } from \"src/sql/indexes.js\";\nimport { PgIdentifier } from \"src/sql/pg-identifier.js\";\nimport type { SortContext } from \"../sql/analyzer.js\";\nimport { PostgresQueryBuilder } from \"../sql/builder.js\";\nimport {\n dropIndex,\n type Postgres,\n type PostgresExplainResult,\n type PostgresExplainStage,\n type PostgresTransaction,\n} from \"../sql/database.js\";\nimport type { IndexedTable, Statistics } from \"./statistics.js\";\n\nexport type IndexIdentifier = string;\n\nexport type IndexRecommendation = PermutedIndexCandidate & {\n definition: IndexIdentifier;\n};\n\ntype Color = (a: string) => string;\n\ntype IndexToCreate = PermutedIndexCandidate & {\n name: PgIdentifier;\n definition: IndexIdentifier;\n};\n\nexport class IndexOptimizer {\n static prefix = \"__qd_\";\n\n constructor(\n private readonly db: Postgres,\n private readonly statistics: Statistics,\n private existingIndexes: IndexedTable[],\n private readonly config: {\n trace?: boolean;\n debug?: boolean;\n } = {},\n ) {}\n\n async run(\n builder: PostgresQueryBuilder,\n indexes: RootIndexCandidate[],\n beforeQuery?: (tx: PostgresTransaction) => Promise<void>,\n ): Promise<OptimizeResult> {\n const baseExplain = await this.testQueryWithStats(builder, async (tx) => {\n if (beforeQuery) {\n await beforeQuery(tx);\n }\n });\n const baseCost: number = Number(baseExplain.Plan[\"Total Cost\"]);\n if (baseCost === 0) {\n return {\n kind: \"zero_cost_plan\",\n explainPlan: baseExplain.Plan,\n };\n }\n const toCreate = this.indexesToCreate(indexes);\n const finalExplain = await this.testQueryWithStats(builder, async (tx) => {\n if (beforeQuery) {\n await beforeQuery(tx);\n }\n\n // Then create recommended indexes\n for (const permutation of toCreate) {\n const createIndex = PostgresQueryBuilder.createIndex(\n permutation.definition,\n permutation.name,\n )\n .introspect()\n .build();\n\n await tx.exec(createIndex);\n }\n });\n const finalCost = Number(finalExplain.Plan[\"Total Cost\"]);\n if (this.config.debug) {\n console.dir(finalExplain, { depth: null });\n }\n const deltaPercentage = ((baseCost - finalCost) / baseCost) * 100;\n if (finalCost < baseCost) {\n console.log(\n ` 🎉🎉🎉 ${green(`+${deltaPercentage.toFixed(2).padStart(5, \"0\")}%`)}`,\n );\n } else if (finalCost > baseCost) {\n console.log(\n `${red(\n `-${Math.abs(deltaPercentage).toFixed(2).padStart(5, \"0\")}%`,\n )} ${gray(\"If there's a better index, we haven't tried it\")}`,\n );\n }\n const baseIndexes = this.findUsedIndexes(baseExplain.Plan);\n const finalIndexes = this.findUsedIndexes(finalExplain.Plan);\n const triedIndexes = new Map(\n toCreate.map((index) => [index.name.toString(), index]),\n );\n this.replaceUsedIndexesWithDefinition(finalExplain.Plan, triedIndexes);\n\n return {\n kind: \"ok\",\n baseCost,\n finalCost,\n newIndexes: finalIndexes.newIndexes,\n existingIndexes: baseIndexes.existingIndexes,\n triedIndexes,\n baseExplainPlan: baseExplain.Plan,\n explainPlan: finalExplain.Plan,\n };\n }\n\n async runWithoutIndexes(builder: PostgresQueryBuilder) {\n return await this.testQueryWithStats(builder, async (tx) => {\n await this.dropExistingIndexes(tx);\n });\n }\n\n /**\n * Given the current indexes in the optimizer, transform them in some\n * way to change which indexes will be assumed to exist when optimizing\n *\n * @example\n * ```\n * // resets indexes\n * optimizer.transformIndexes(() => [])\n *\n * // adds new index\n * optimizer.transformIndexes(indexes => [...indexes, newIndex])\n * ```\n */\n transformIndexes(f: (indexes: IndexedTable[]) => IndexedTable[]) {\n const newIndexes = f(this.existingIndexes);\n this.existingIndexes = newIndexes;\n return this;\n }\n\n /**\n * Postgres has a limit of 63 characters for index names.\n * So we use this to make sure we don't derive it from a list of columns that can\n * overflow that limit.\n */\n private indexName(): PgIdentifier {\n const indexName =\n IndexOptimizer.prefix + Math.random().toString(36).substring(2, 16);\n return PgIdentifier.fromString(indexName);\n }\n\n // TODO: this doesn't belong in the optimizer\n private indexAlreadyExists(\n table: string,\n columns: RootIndexCandidate[],\n ): IndexedTable | undefined {\n return this.existingIndexes.find(\n (index) =>\n index.index_type === \"btree\" &&\n index.table_name === table &&\n index.index_columns.length === columns.length &&\n index.index_columns.every((c, i) => {\n if (columns[i].column !== c.name) {\n return false;\n }\n\n // we should assume any index with `WHERE`\n // can't be counted as a duplicate\n if (columns[i].where) {\n return false;\n }\n\n if (columns[i].sort) {\n switch (columns[i].sort.dir) {\n // Sorting is ASC by default in postgres\n case \"SORTBY_DEFAULT\":\n case \"SORTBY_ASC\":\n if (c.order !== \"ASC\") {\n return false;\n }\n break;\n case \"SORTBY_DESC\":\n if (c.order !== \"DESC\") {\n return false;\n }\n break;\n }\n }\n return true;\n }),\n );\n }\n\n /**\n * Derive the list of indexes [tableA(X, Y, Z), tableB(H, I, J)]\n **/\n private indexesToCreate(\n rootCandidates: RootIndexCandidate[],\n ): IndexToCreate[] {\n const permutedIndexes = this.tableColumnIndexCandidates(rootCandidates);\n const nextStage: IndexToCreate[] = [];\n for (const permutation of permutedIndexes.values()) {\n const { table: rawTable, schema: rawSchema, columns } = permutation;\n const permutations = permuteWithFeedback(columns);\n let iter = permutations.next(PROCEED);\n while (!iter.done) {\n const columns = iter.value;\n // TODO: accept PgIdentifier values instead\n // required refactoring `PermutedIndexCandidate`\n const schema = PgIdentifier.fromString(rawSchema);\n const table = PgIdentifier.fromString(rawTable);\n const existingIndex = this.indexAlreadyExists(\n table.toString(),\n columns,\n );\n if (existingIndex) {\n iter = permutations.next(PROCEED);\n continue;\n }\n const indexName = this.indexName();\n\n const definition = this.toDefinition({ table, schema, columns }).raw;\n\n iter = permutations.next(PROCEED);\n nextStage.push({\n name: indexName,\n schema: schema.toString(),\n table: table.toString(),\n columns,\n definition,\n });\n }\n }\n return nextStage;\n }\n\n private toDefinition({\n schema,\n table,\n columns,\n }: {\n schema: PgIdentifier;\n table: PgIdentifier;\n columns: RootIndexCandidate[];\n }) {\n const make = (col: Color, order: Color, where: Color, keyword: Color) => {\n // TODO: refactor all of this class to accept PgIdentifiers\n let fullyQualifiedTable: PgIdentifier;\n\n if (schema.toString() === \"public\") {\n fullyQualifiedTable = table;\n } else {\n fullyQualifiedTable = PgIdentifier.fromParts(schema, table);\n }\n const baseColumn = `${fullyQualifiedTable}(${columns\n .map((c) => {\n const column = PgIdentifier.fromString(c.column);\n const direction = c.sort && this.sortDirection(c.sort);\n const nulls = c.sort && this.nullsOrder(c.sort);\n let sort = col(column.toString());\n if (direction) {\n sort += ` ${order(direction)}`;\n }\n if (nulls) {\n sort += ` ${order(nulls)}`;\n }\n return sort;\n })\n .join(\", \")})`;\n // TODO: add support for generating partial indexes\n // if (clauses.length > 0) {\n // return `${baseColumn} ${where(\"where\")} ${clauses.join(\" and \")}`;\n // }\n return baseColumn;\n };\n const id: Color = (a) => a;\n const raw = make(id, id, id, id);\n const colored = make(green, yellow, magenta, blue);\n return { raw, colored };\n }\n\n /**\n * Drop indexes that can be dropped. Ignore the ones that can't\n */\n private async dropExistingIndexes(tx: PostgresTransaction) {\n for (const index of this.existingIndexes) {\n if (!isIndexProbablyDroppable(index)) {\n continue;\n }\n const indexName = PgIdentifier.fromParts(\n index.schema_name,\n index.index_name,\n );\n await dropIndex(tx, indexName);\n }\n }\n\n private whereClause(c: RootIndexCandidate, col: Color, keyword: Color) {\n if (!c.where) {\n return \"\";\n }\n if (c.where.nulltest === \"IS_NULL\") {\n return `${col(`\"${c.column}\"`)} is ${keyword(\"null\")}`;\n }\n if (c.where.nulltest === \"IS_NOT_NULL\") {\n return `${col(`\"${c.column}\"`)} is not ${keyword(\"null\")}`;\n }\n return \"\";\n }\n\n private nullsOrder(s: SortContext) {\n if (!s.nulls) {\n return \"\";\n }\n switch (s.nulls) {\n case \"SORTBY_NULLS_FIRST\":\n return \"nulls first\";\n case \"SORTBY_NULLS_LAST\":\n return \"nulls last\";\n case \"SORTBY_NULLS_DEFAULT\":\n default:\n return \"\";\n }\n }\n\n private sortDirection(s: SortContext) {\n if (!s.dir) {\n return \"\";\n }\n switch (s.dir) {\n case \"SORTBY_DESC\":\n return \"desc\";\n case \"SORTBY_ASC\":\n return \"asc\";\n case \"SORTBY_DEFAULT\":\n // god help us if we ever run into this\n case \"SORTBY_USING\":\n default:\n return \"\";\n }\n }\n\n async testQueryWithStats(\n builder: PostgresQueryBuilder,\n f?: (tx: PostgresTransaction) => Promise<void>,\n options?: { params?: unknown[]; genericPlan?: boolean },\n ): Promise<{ Plan: PostgresExplainStage }> {\n try {\n await this.db.transaction(async (tx) => {\n await f?.(tx);\n await this.statistics.restoreStats(tx);\n const flags = [\"format json\"];\n if (options && !options.genericPlan) {\n flags.push(\"analyze\");\n if (this.config.trace) {\n // trace can only be used alongside analyze\n // since it depends on the results of the query execution\n flags.push(\"trace\");\n }\n } else {\n flags.push(\"generic_plan\");\n }\n const { commands, query } = builder.explain(flags).buildParts();\n // this is done in a separate step to prevent sending multiple commands when using parameters\n await tx.exec(commands);\n const result = await tx.exec<PostgresExplainResult>(\n query,\n options?.params,\n );\n const explain = result[0][\"QUERY PLAN\"][0];\n throw new RollbackError(explain);\n });\n } catch (error) {\n if (error instanceof RollbackError) {\n return error.value;\n }\n throw error;\n }\n throw new Error(\"Unreachable\");\n }\n\n private tableColumnIndexCandidates(indexes: RootIndexCandidate[]) {\n const tableColumns: Map<\n string,\n { schema: string; table: string; columns: RootIndexCandidate[] }\n > = new Map();\n for (const index of indexes) {\n const existing = tableColumns.get(`${index.schema}.${index.table}`);\n if (existing) {\n existing.columns.push(index);\n } else {\n tableColumns.set(`${index.schema}.${index.table}`, {\n table: index.table,\n schema: index.schema,\n columns: [index],\n });\n }\n }\n return tableColumns;\n }\n\n private findUsedIndexes(explain: Record<string, any>) {\n const newIndexes: Set<string> = new Set();\n const existingIndexes: Set<string> = new Set();\n const prefix = IndexOptimizer.prefix;\n walkExplain(explain, (stage) => {\n const indexName = stage[\"Index Name\"];\n if (indexName) {\n // Check for prefix at start or embedded (for hypertable chunk indexes like _hyper_1_1_chunk___qd_xxx)\n if (indexName.startsWith(prefix)) {\n newIndexes.add(indexName);\n } else if (indexName.includes(prefix)) {\n // Extract the actual index name from chunk-prefixed names (e.g., _hyper_1_1_chunk___qd_xxx -> __qd_xxx)\n const actualName = indexName.substring(indexName.indexOf(prefix));\n newIndexes.add(actualName);\n } else {\n existingIndexes.add(indexName);\n }\n }\n });\n return {\n newIndexes,\n existingIndexes,\n };\n }\n\n private replaceUsedIndexesWithDefinition(\n explain: Record<string, any>,\n triedIndexes: Map<string, IndexRecommendation>,\n ) {\n walkExplain(explain, (stage) => {\n const indexName = stage[\"Index Name\"];\n if (typeof indexName === \"string\") {\n const recommendation = triedIndexes.get(indexName);\n if (recommendation) {\n stage[\"Index Name\"] = recommendation.definition;\n }\n }\n });\n }\n}\n\nfunction walkExplain(explain: Record<string, any>, f: (stage: any) => void) {\n function go(plan: any) {\n f(plan);\n if (plan.Plans) {\n for (const p of plan.Plans) {\n go(p);\n }\n }\n }\n go(explain);\n}\n\nexport type OptimizeResult =\n | {\n kind: \"ok\";\n baseExplainPlan: PostgresExplainStage;\n baseCost: number;\n finalCost: number;\n newIndexes: Set<string>;\n existingIndexes: Set<string>;\n triedIndexes: Map<string, IndexRecommendation>;\n explainPlan: PostgresExplainStage;\n }\n | {\n kind: \"zero_cost_plan\";\n explainPlan: PostgresExplainStage;\n };\n\nclass RollbackError<T> {\n constructor(public readonly value?: T) {}\n}\n\nexport type RootIndexCandidate = {\n schema: string;\n table: string;\n column: string;\n sort?: SortContext;\n where?: { nulltest?: NullTestType };\n};\n\nexport type PermutedIndexCandidate = {\n schema: string;\n table: string;\n columns: RootIndexCandidate[];\n // TODO: functional indexes\n where?: string;\n};\n\nexport const PROCEED = Symbol(\"PROCEED\");\nexport const SKIP = Symbol(\"SKIP\");\n\n/**\n * Allows permuting over an array of items.\n * The generator allows the caller to prematurely stop the permutation chain.\n */\nexport function* permuteWithFeedback<T>(\n arr: T[],\n): Generator<T[], void, typeof PROCEED | typeof SKIP> {\n function* helper(\n path: T[],\n rest: T[],\n ): Generator<T[], void, typeof PROCEED | typeof SKIP> {\n let i = 0;\n while (i < rest.length) {\n const nextPath = [...path, rest[i]];\n const nextRest = [...rest.slice(0, i), ...rest.slice(i + 1)];\n const input = yield nextPath;\n\n if (input === PROCEED) {\n yield* helper(nextPath, nextRest);\n }\n\n i++;\n }\n }\n\n yield* helper([], arr);\n}\n","import { gray } from \"colorette\";\nimport dedent from \"dedent\";\nimport { z } from \"zod\";\nimport type {\n Postgres,\n PostgresTransaction,\n PostgresVersion,\n} from \"../sql/database.ts\";\n\ntype StaValueKind = \"real\" | \"text\" | \"boolean\" | null;\n\nexport type Path = string;\n\nexport const StatisticsSource = z.union([\n z.object({\n kind: z.literal(\"path\"),\n path: z.string().min(1),\n }),\n z.object({\n kind: z.literal(\"inline\"),\n }),\n]);\n\nexport const ExportedStatsStatistics = z.object({\n stawidth: z.number(),\n stainherit: z.boolean().default(false),\n // 0 representing unknown\n stadistinct: z.number(),\n // this has no \"nullable\" state\n stanullfrac: z.number(),\n stakind1: z.number().min(0),\n stakind2: z.number().min(0),\n stakind3: z.number().min(0),\n stakind4: z.number().min(0),\n stakind5: z.number().min(0),\n staop1: z.string(),\n staop2: z.string(),\n staop3: z.string(),\n staop4: z.string(),\n staop5: z.string(),\n stacoll1: z.string(),\n stacoll2: z.string(),\n stacoll3: z.string(),\n stacoll4: z.string(),\n stacoll5: z.string(),\n stanumbers1: z.array(z.number()).nullable(),\n stanumbers2: z.array(z.number()).nullable(),\n stanumbers3: z.array(z.number()).nullable(),\n stanumbers4: z.array(z.number()).nullable(),\n stanumbers5: z.array(z.number()).nullable(),\n // theoretically... this could only be strings and numbers\n // but we don't have a crystal ball\n stavalues1: z.array(z.any()).nullable(),\n stavalues2: z.array(z.any()).nullable(),\n stavalues3: z.array(z.any()).nullable(),\n stavalues4: z.array(z.any()).nullable(),\n stavalues5: z.array(z.any()).nullable(),\n});\n\nexport const ExportedStatsColumns = z.object({\n columnName: z.string(),\n stats: ExportedStatsStatistics.nullable(),\n});\n\nexport const ExportedStatsIndex = z.object({\n indexName: z.string(),\n relpages: z.number(),\n reltuples: z.number(),\n relallvisible: z.number(),\n relallfrozen: z.number().optional(),\n});\n\n// This should match the output of the `_qd_dump_stats` function in the analyzer README.md\n// Need to make sure this is versioned to accept ALL potential outputs from every version of\n// dump functions we make public\nexport const ExportedStatsV1 = z.object({\n tableName: z.string(),\n schemaName: z.string(),\n // can be negative\n relpages: z.number(),\n // can be negative\n reltuples: z.number(),\n relallvisible: z.number(),\n // only postgres 18+\n relallfrozen: z.number().optional(),\n columns: z.array(ExportedStatsColumns).nullable(),\n indexes: z.array(ExportedStatsIndex),\n});\n\nexport const ExportedStats = z.union([ExportedStatsV1]);\n\nexport type ExportedStats = z.infer<typeof ExportedStats>;\n\nexport const StatisticsMode = z.discriminatedUnion(\"kind\", [\n z.object({\n kind: z.literal(\"fromAssumption\"),\n reltuples: z.number().min(0),\n relpages: z.number().min(0),\n }),\n z.object({\n kind: z.literal(\"fromStatisticsExport\"),\n stats: z.array(ExportedStats),\n source: StatisticsSource,\n }),\n]);\n\nexport type StatisticsMode = z.infer<typeof StatisticsMode>;\n\nconst DEFAULT_RELTUPLES = 10_000;\nconst DEFAULT_RELPAGES = 1;\nexport class Statistics {\n readonly mode: StatisticsMode;\n private readonly exportedMetadata: ExportedStats[] | undefined;\n // preventing accidental internal mutations\n static readonly defaultStatsMode: StatisticsMode = Object.freeze({\n kind: \"fromAssumption\",\n reltuples: DEFAULT_RELTUPLES,\n relpages: DEFAULT_RELPAGES,\n });\n constructor(\n private readonly db: Postgres,\n public readonly postgresVersion: PostgresVersion,\n public readonly ownMetadata: ExportedStats[],\n statsMode: StatisticsMode,\n ) {\n if (statsMode) {\n this.mode = statsMode;\n if (statsMode.kind === \"fromStatisticsExport\") {\n this.exportedMetadata = statsMode.stats;\n }\n } else {\n this.mode = Statistics.defaultStatsMode;\n }\n }\n\n static statsModeFromAssumption({\n reltuples,\n relpages,\n }: {\n reltuples: number;\n relpages: number;\n }): StatisticsMode {\n return {\n kind: \"fromAssumption\",\n reltuples,\n relpages,\n };\n }\n\n /**\n * Create a statistic mode from stats exported from another database\n **/\n static statsModeFromExport(stats: ExportedStats[]): StatisticsMode {\n return {\n kind: \"fromStatisticsExport\",\n source: { kind: \"inline\" },\n stats,\n };\n }\n\n static async fromPostgres(\n db: Postgres,\n statsMode: StatisticsMode,\n ): Promise<Statistics> {\n const version = await db.serverNum();\n const ownStats = await Statistics.dumpStats(db, version, \"full\");\n return new Statistics(db, version, ownStats, statsMode);\n }\n\n restoreStats(tx: PostgresTransaction) {\n // if (this.postgresVersion < \"180000\") {\n return this.restoreStats17(tx);\n // }\n // return this.restoreStats18(tx);\n }\n\n approximateTotalRows() {\n if (!this.exportedMetadata) {\n return 0;\n }\n let totalRows = 0;\n for (const table of this.exportedMetadata) {\n totalRows += table.reltuples;\n }\n return totalRows;\n }\n\n /**\n * We have to cast stavaluesN to the correct type\n * This derives that type for us so it can be used in `array_in`\n */\n private stavalueKind(values: unknown[] | null): StaValueKind {\n if (!values || values.length === 0) {\n return null;\n }\n const [elem] = values;\n if (typeof elem === \"number\") {\n return \"real\";\n } else if (typeof elem === \"boolean\") {\n return \"boolean\";\n }\n // is everything else a text? What about strinfied dates?\n // we might need column metadata access here if we do\n return \"text\";\n }\n\n private async restoreStats17(tx: PostgresTransaction) {\n const warnings = {\n tablesNotInExports: [] as string[],\n tablesNotInTest: [] as string[],\n tableNotAnalyzed: [] as string[],\n statsMissing: [] as {\n statistic: string;\n table: string;\n schema: string;\n column: string;\n }[],\n };\n const processedTables = new Set<string>();\n\n let columnStatsUpdatePromise: Promise<any> | undefined;\n const columnStatsValues: Array<{\n schema_name: string;\n table_name: string;\n column_name: string;\n stainherit: boolean;\n stanullfrac: number;\n stawidth: number;\n stadistinct: number;\n stakind1: number;\n stakind2: number;\n stakind3: number;\n stakind4: number;\n stakind5: number;\n staop1: string;\n staop2: string;\n staop3: string;\n staop4: string;\n staop5: string;\n stacoll1: string;\n stacoll2: string;\n stacoll3: string;\n stacoll4: string;\n stacoll5: string;\n stanumbers1: number[] | null;\n stanumbers2: number[] | null;\n stanumbers3: number[] | null;\n stanumbers4: number[] | null;\n stanumbers5: number[] | null;\n stavalues1: any[] | null;\n stavalues2: any[] | null;\n stavalues3: any[] | null;\n stavalues4: any[] | null;\n stavalues5: any[] | null;\n _value_type1: StaValueKind;\n _value_type2: StaValueKind;\n _value_type3: StaValueKind;\n _value_type4: StaValueKind;\n _value_type5: StaValueKind;\n }> = [];\n if (this.exportedMetadata) {\n for (const table of this.ownMetadata) {\n const targetTable = this.exportedMetadata.find(\n (m) =>\n m.tableName === table.tableName &&\n m.schemaName === table.schemaName,\n );\n if (!targetTable?.columns) {\n continue;\n }\n for (const column of targetTable.columns) {\n const { stats } = column;\n if (!stats) {\n continue;\n }\n // TODO: track processed columns too\n columnStatsValues.push({\n schema_name: table.schemaName,\n table_name: table.tableName,\n column_name: column.columnName,\n stainherit: stats.stainherit ?? false,\n stanullfrac: stats.stanullfrac,\n stawidth: stats.stawidth,\n stadistinct: stats.stadistinct,\n stakind1: stats.stakind1,\n stakind2: stats.stakind2,\n stakind3: stats.stakind3,\n stakind4: stats.stakind4,\n stakind5: stats.stakind5,\n staop1: stats.staop1,\n staop2: stats.staop2,\n staop3: stats.staop3,\n staop4: stats.staop4,\n staop5: stats.staop5,\n stacoll1: stats.stacoll1,\n stacoll2: stats.stacoll2,\n stacoll3: stats.stacoll3,\n stacoll4: stats.stacoll4,\n stacoll5: stats.stacoll5,\n stanumbers1: stats.stanumbers1,\n stanumbers2: stats.stanumbers2,\n stanumbers3: stats.stanumbers3,\n stanumbers4: stats.stanumbers4,\n stanumbers5: stats.stanumbers5,\n stavalues1: stats.stavalues1,\n stavalues2: stats.stavalues2,\n stavalues3: stats.stavalues3,\n stavalues4: stats.stavalues4,\n stavalues5: stats.stavalues5,\n _value_type1: this.stavalueKind(stats.stavalues1),\n _value_type2: this.stavalueKind(stats.stavalues2),\n _value_type3: this.stavalueKind(stats.stavalues3),\n _value_type4: this.stavalueKind(stats.stavalues4),\n _value_type5: this.stavalueKind(stats.stavalues5),\n });\n }\n }\n /**\n * Postgres has 5 different slots for storing statistics per column and a potentially unlimited\n * number of statistic types to choose from. Each code in `stakindN` can mean different things.\n * Some statistics are just numerical values such as `n_distinct` and `correlation`, meaning\n * they're only derived from `stanumbersN` and the value of `stanumbersN` is never read.\n * Others take advantage of the `stavaluesN` columns which use `anyarray` type to store\n * concrete values internally for things like histogram bounds.\n * Unfortunately we cannot change anyarrays without a C extension.\n *\n * (1) = most common values\n * (2) = scalar histogram\n * (3) = correlation <- can change\n * (4) = most common elements\n * (5) = distinct elem count histogram <- can change\n * (6) = length histogram (?) These don't appear in pg_stats\n * (7) = bounds histogram (?) These don't appear in pg_stats\n * (N) = potentially many more kinds of statistics. But postgres <=18 only uses these 7.\n *\n * What we're doing here is setting ANY statistic we cannot directly control\n * (anything that relies on stavaluesN) to 0 to make sure the planner isn't influenced by what\n * what the db collected from the test data.\n * Because we do our tests with `generic_plan` it seems it's already unlikely that the planner will be\n * using things like common values or histogram bounds to make the planning decisions we care about.\n * This is a just in case.\n */\n const sql = dedent`\n WITH input AS (\n SELECT\n c.oid AS starelid,\n a.attnum AS staattnum,\n v.stainherit,\n v.stanullfrac,\n v.stawidth,\n v.stadistinct,\n v.stakind1,\n v.stakind2,\n v.stakind3,\n v.stakind4,\n v.stakind5,\n v.staop1,\n v.staop2,\n v.staop3,\n v.staop4,\n v.staop5,\n v.stacoll1,\n v.stacoll2,\n v.stacoll3,\n v.stacoll4,\n v.stacoll5,\n v.stanumbers1,\n v.stanumbers2,\n v.stanumbers3,\n v.stanumbers4,\n v.stanumbers5,\n v.stavalues1,\n v.stavalues2,\n v.stavalues3,\n v.stavalues4,\n v.stavalues5,\n _value_type1,\n _value_type2,\n _value_type3,\n _value_type4,\n _value_type5\n FROM jsonb_to_recordset($1::jsonb) AS v(\n schema_name text,\n table_name text,\n column_name text,\n stainherit boolean,\n stanullfrac real,\n stawidth integer,\n stadistinct real,\n stakind1 real,\n stakind2 real,\n stakind3 real,\n stakind4 real,\n stakind5 real,\n staop1 oid,\n staop2 oid,\n staop3 oid,\n staop4 oid,\n staop5 oid,\n stacoll1 oid,\n stacoll2 oid,\n stacoll3 oid,\n stacoll4 oid,\n stacoll5 oid,\n stanumbers1 real[],\n stanumbers2 real[],\n stanumbers3 real[],\n stanumbers4 real[],\n stanumbers5 real[],\n stavalues1 text[],\n stavalues2 text[],\n stavalues3 text[],\n stavalues4 text[],\n stavalues5 text[],\n _value_type1 text,\n _value_type2 text,\n _value_type3 text,\n _value_type4 text,\n _value_type5 text\n )\n JOIN pg_class c ON c.relname = v.table_name\n JOIN pg_namespace n ON n.oid = c.relnamespace AND n.nspname = v.schema_name\n JOIN pg_attribute a ON a.attrelid = c.oid AND a.attname = v.column_name\n ),\n updated AS (\n UPDATE pg_statistic s\n SET\n stanullfrac = i.stanullfrac,\n stawidth = i.stawidth,\n stadistinct = i.stadistinct,\n stakind1 = i.stakind1,\n stakind2 = i.stakind2,\n stakind3 = i.stakind3,\n stakind4 = i.stakind4,\n stakind5 = i.stakind5,\n staop1 = i.staop1,\n staop2 = i.staop2,\n staop3 = i.staop3,\n staop4 = i.staop4,\n staop5 = i.staop5,\n stacoll1 = i.stacoll1,\n stacoll2 = i.stacoll2,\n stacoll3 = i.stacoll3,\n stacoll4 = i.stacoll4,\n stacoll5 = i.stacoll5,\n stanumbers1 = i.stanumbers1,\n stanumbers2 = i.stanumbers2,\n stanumbers3 = i.stanumbers3,\n stanumbers4 = i.stanumbers4,\n stanumbers5 = i.stanumbers5,\n stavalues1 = case\n when i.stavalues1 is null then null\n else array_in(i.stavalues1::text::cstring, i._value_type1::regtype::oid, -1)\n end,\n stavalues2 = case\n when i.stavalues2 is null then null\n else array_in(i.stavalues2::text::cstring, i._value_type2::regtype::oid, -1)\n end,\n stavalues3 = case\n when i.stavalues3 is null then null\n else array_in(i.stavalues3::text::cstring, i._value_type3::regtype::oid, -1)\n end,\n stavalues4 = case\n when i.stavalues4 is null then null\n else array_in(i.stavalues4::text::cstring, i._value_type4::regtype::oid, -1)\n end,\n stavalues5 = case\n when i.stavalues5 is null then null\n else array_in(i.stavalues5::text::cstring, i._value_type5::regtype::oid, -1)\n end\n -- stavalues1 = i.stavalues1,\n -- stavalues2 = i.stavalues2,\n -- stavalues3 = i.stavalues3,\n -- stavalues4 = i.stavalues4,\n -- stavalues5 = i.stavalues5\n FROM input i\n WHERE s.starelid = i.starelid AND s.staattnum = i.staattnum AND s.stainherit = i.stainherit\n RETURNING s.starelid, s.staattnum, s.stainherit, s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5\n ),\n inserted as (\n INSERT INTO pg_statistic (\n starelid, staattnum, stainherit,\n stanullfrac, stawidth, stadistinct,\n stakind1, stakind2, stakind3, stakind4, stakind5,\n staop1, staop2, staop3, staop4, staop5,\n stacoll1, stacoll2, stacoll3, stacoll4, stacoll5,\n stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5,\n stavalues1, stavalues2, stavalues3, stavalues4, stavalues5\n )\n SELECT\n i.starelid, i.staattnum, i.stainherit,\n i.stanullfrac, i.stawidth, i.stadistinct,\n i.stakind1, i.stakind2, i.stakind3, i.stakind4, i.stakind5,\n i.staop1, i.staop2, i.staop3, i.staop4, i.staop5,\n i.stacoll1, i.stacoll2, i.stacoll3, i.stacoll4, i.stacoll5,\n i.stanumbers1, i.stanumbers2, i.stanumbers3, i.stanumbers4, i.stanumbers5,\n -- i.stavalues1, i.stavalues2, i.stavalues3, i.stavalues4, i.stavalues5,\n case\n when i.stavalues1 is null then null\n else array_in(i.stavalues1::text::cstring, i._value_type1::regtype::oid, -1)\n end,\n case\n when i.stavalues2 is null then null\n else array_in(i.stavalues2::text::cstring, i._value_type2::regtype::oid, -1)\n end,\n case\n when i.stavalues3 is null then null\n else array_in(i.stavalues3::text::cstring, i._value_type3::regtype::oid, -1)\n end,\n case\n when i.stavalues4 is null then null\n else array_in(i.stavalues4::text::cstring, i._value_type4::regtype::oid, -1)\n end,\n case\n when i.stavalues5 is null then null\n else array_in(i.stavalues5::text::cstring, i._value_type5::regtype::oid, -1)\n end\n -- i._value_type1, i._value_type2, i._value_type3, i._value_type4, i._value_type5\n FROM input i\n LEFT JOIN updated u\n ON i.starelid = u.starelid AND i.staattnum = u.staattnum AND i.stainherit = u.stainherit\n WHERE u.starelid IS NULL\n returning starelid, staattnum, stainherit, stakind1, stakind2, stakind3, stakind4, stakind5\n )\n select * from updated union all (select * from inserted); -- @qd_introspection`;\n\n columnStatsUpdatePromise = tx\n .exec(sql, [columnStatsValues])\n .catch((err) => {\n console.error(\"Something wrong wrong updating column stats\");\n console.error(err);\n throw err;\n // return err;\n // return Promise.reject(err)\n });\n }\n\n const reltuplesValues: Array<{\n reltuples: number;\n relpages: number;\n relname: string;\n schema_name: string;\n relallvisible: number;\n relallfrozen?: number;\n }> = [];\n for (const table of this.ownMetadata) {\n if (!table.columns) {\n continue;\n }\n processedTables.add(`${table.schemaName}.${table.tableName}`);\n let targetTable: ExportedStats | undefined;\n if (this.exportedMetadata) {\n targetTable = this.exportedMetadata.find(\n (m) =>\n m.tableName === table.tableName &&\n m.schemaName === table.schemaName,\n );\n }\n let reltuples: number;\n let relpages: number;\n let relallvisible: number = 0;\n let relallfrozen: number | undefined;\n if (targetTable) {\n // don't want to run our prod stats with -1 reltuples\n // we warn the user about this later\n // if (targetTable.reltuples < 10 || targetTable.reltuples > 10000) {\n reltuples = targetTable.reltuples;\n relpages = targetTable.relpages;\n relallvisible = targetTable.relallvisible;\n relallfrozen = targetTable.relallfrozen;\n // }\n } else if (this.mode.kind === \"fromAssumption\") {\n reltuples = this.mode.reltuples;\n relpages = this.mode.relpages;\n } else {\n // we want to warn about tables that are in the test but not in the exported stats\n // this can happen in case a new table is created in a PR\n warnings.tablesNotInExports.push(\n `${table.schemaName}.${table.tableName}`,\n );\n reltuples = DEFAULT_RELTUPLES;\n relpages = DEFAULT_RELPAGES;\n }\n reltuplesValues.push({\n relname: table.tableName,\n schema_name: table.schemaName,\n reltuples,\n relpages,\n relallfrozen,\n relallvisible,\n });\n if (targetTable && targetTable.indexes) {\n for (const index of targetTable.indexes) {\n reltuplesValues.push({\n relname: index.indexName,\n schema_name: targetTable.schemaName,\n reltuples: index.reltuples,\n relpages: index.relpages,\n relallfrozen: index.relallfrozen,\n relallvisible: index.relallvisible,\n });\n }\n }\n }\n\n const reltuplesQuery = dedent`\n update pg_class p\n set reltuples = v.reltuples,\n relpages = v.relpages,\n -- relallfrozen = case when v.relallfrozen is null then p.relallfrozen else v.relallfrozen end,\n relallvisible = case when v.relallvisible is null then p.relallvisible else v.relallvisible end\n from jsonb_to_recordset($1::jsonb)\n as v(reltuples real, relpages integer, relallfrozen integer, relallvisible integer, relname text, schema_name text)\n where p.relname = v.relname\n and p.relnamespace = (select oid from pg_namespace where nspname = v.schema_name)\n returning p.relname, p.relnamespace, p.reltuples, p.relpages;\n `;\n\n const reltuplesPromise = tx\n .exec(reltuplesQuery, [reltuplesValues])\n .catch((err) => {\n console.error(\"Something went wrong updating reltuples/relpages\");\n console.error(err);\n return err;\n });\n\n if (this.exportedMetadata) {\n for (const table of this.exportedMetadata) {\n const tableExists = processedTables.has(\n `${table.schemaName}.${table.tableName}`,\n );\n if (tableExists && table.reltuples === -1) {\n console.warn(\n `Table ${table.tableName} has reltuples -1. Your production database is probably not analyzed properly`,\n );\n // we expect production stats to have real numbers\n warnings.tableNotAnalyzed.push(\n `${table.schemaName}.${table.tableName}`,\n );\n }\n if (tableExists) {\n continue;\n }\n // there's a LOT of tables in statistics exports for things like timescaledb\n // that might not show up in the test data. This check might be too strict.\n warnings.tablesNotInTest.push(`${table.schemaName}.${table.tableName}`);\n }\n }\n const [statsUpdates, reltuplesUpdates] = await Promise.all([\n columnStatsUpdatePromise,\n reltuplesPromise,\n ]);\n const updatedColumnsProperly = statsUpdates\n ? statsUpdates.length === columnStatsValues.length\n : true;\n if (!updatedColumnsProperly) {\n console.error(`Did not update expected column stats`);\n }\n if (reltuplesUpdates.length !== reltuplesValues.length) {\n console.error(`Did not update expected reltuples/relpages`);\n }\n return warnings;\n }\n\n static async dumpStats(\n db: PostgresTransaction,\n postgresVersion: PostgresVersion,\n kind: \"anonymous\" | \"full\",\n ): Promise<ExportedStats[]> {\n const fullDump = kind === \"full\";\n console.log(`dumping stats for postgres ${gray(postgresVersion)}`);\n // certain things are only supported with pg17\n const stats = await db.exec<{ json_agg: ExportedStats[] }>(\n `\n WITH table_columns AS (\n SELECT\n c.table_name,\n c.table_schema,\n cl.reltuples,\n cl.relpages,\n cl.relallvisible,\n -- cl.relallfrozen,\n n.nspname AS schema_name,\n json_agg(\n json_build_object(\n 'columnName', c.column_name,\n 'stats', (\n SELECT json_build_object(\n 'starelid', s.starelid,\n 'staattnum', s.staattnum,\n 'stanullfrac', s.stanullfrac,\n 'stawidth', s.stawidth,\n 'stadistinct', s.stadistinct,\n 'stakind1', s.stakind1, 'staop1', s.staop1, 'stacoll1', s.stacoll1, 'stanumbers1', s.stanumbers1,\n 'stakind2', s.stakind2, 'staop2', s.staop2, 'stacoll2', s.stacoll2, 'stanumbers2', s.stanumbers2,\n 'stakind3', s.stakind3, 'staop3', s.staop3, 'stacoll3', s.stacoll3, 'stanumbers3', s.stanumbers3,\n 'stakind4', s.stakind4, 'staop4', s.staop4, 'stacoll4', s.stacoll4, 'stanumbers4', s.stanumbers4,\n 'stakind5', s.stakind5, 'staop5', s.staop5, 'stacoll5', s.stacoll5, 'stanumbers5', s.stanumbers5,\n 'stavalues1', CASE WHEN $1 THEN s.stavalues1 ELSE NULL END,\n 'stavalues2', CASE WHEN $1 THEN s.stavalues2 ELSE NULL END,\n 'stavalues3', CASE WHEN $1 THEN s.stavalues3 ELSE NULL END,\n 'stavalues4', CASE WHEN $1 THEN s.stavalues4 ELSE NULL END,\n 'stavalues5', CASE WHEN $1 THEN s.stavalues5 ELSE NULL END\n )\n FROM pg_statistic s\n WHERE s.starelid = a.attrelid AND s.staattnum = a.attnum\n )\n )\n ORDER BY c.ordinal_position\n ) AS columns\n FROM information_schema.columns c\n JOIN pg_attribute a\n ON a.attrelid = (quote_ident(c.table_schema) || '.' || quote_ident(c.table_name))::regclass\n AND a.attname = c.column_name\n JOIN pg_class cl\n ON cl.oid = a.attrelid\n JOIN pg_namespace n\n ON n.oid = cl.relnamespace\n WHERE c.table_name NOT LIKE 'pg_%'\n AND n.nspname <> 'information_schema'\n AND c.table_name NOT IN ('pg_stat_statements', 'pg_stat_statements_info')\n GROUP BY c.table_name, c.table_schema, cl.reltuples, cl.relpages, cl.relallvisible, n.nspname\n ),\n table_indexes AS (\n SELECT\n t.relname AS table_name,\n json_agg(\n json_build_object(\n 'indexName', i.relname,\n 'reltuples', i.reltuples,\n 'relpages', i.relpages,\n 'relallvisible', i.relallvisible\n -- 'relallfrozen', i.relallfrozen\n )\n ) AS indexes\n FROM pg_class t\n JOIN pg_index ix ON ix.indrelid = t.oid\n JOIN pg_class i ON i.oid = ix.indexrelid\n JOIN pg_namespace n ON n.oid = t.relnamespace\n WHERE t.relname NOT LIKE 'pg_%'\n AND n.nspname <> 'information_schema'\n GROUP BY t.relname\n )\n SELECT json_agg(\n json_build_object(\n 'tableName', tc.table_name,\n 'schemaName', tc.table_schema,\n 'reltuples', tc.reltuples,\n 'relpages', tc.relpages,\n 'relallvisible', tc.relallvisible,\n -- 'relallfrozen', tc.relallfrozen,\n 'columns', tc.columns,\n 'indexes', COALESCE(ti.indexes, '[]'::json)\n )\n )\n FROM table_columns tc\n LEFT JOIN table_indexes ti\n ON ti.table_name = tc.table_name;\n `,\n [fullDump],\n );\n return stats[0].json_agg;\n }\n\n /**\n * Returns all indexes in the database.\n * ONLY handles regular btree indexes\n */\n async getExistingIndexes(): Promise<IndexedTable[]> {\n const indexes = await this.db.exec<IndexedTable>(`\n WITH partitioned_tables AS (\n SELECT\n inhparent::regclass AS parent_table,\n inhrelid::regclass AS partition_table\n FROM\n pg_inherits\n )\n SELECT\n n.nspname AS schema_name,\n COALESCE(pt.parent_table::text, t.relname) AS table_name,\n i.relname AS index_name,\n ix.indisprimary as is_primary,\n ix.indisunique as is_unique,\n am.amname AS index_type,\n array_agg(\n CASE\n -- Handle regular columns\n WHEN a.attname IS NOT NULL THEN\n json_build_object('name', a.attname, 'order',\n CASE\n WHEN (indoption[array_position(ix.indkey, a.attnum)] & 1) = 1 THEN 'DESC'\n ELSE 'ASC'\n END)\n -- Handle expressions\n ELSE\n json_build_object('name', pg_get_expr((ix.indexprs)::pg_node_tree, t.oid), 'order',\n CASE\n WHEN (indoption[array_position(ix.indkey, k.attnum)] & 1) = 1 THEN 'DESC'\n ELSE 'ASC'\n END)\n END\n ORDER BY array_position(ix.indkey, k.attnum)\n ) AS index_columns\n FROM\n pg_class t\n LEFT JOIN partitioned_tables pt ON t.oid = pt.partition_table\n JOIN pg_index ix ON t.oid = ix.indrelid\n JOIN pg_class i ON i.oid = ix.indexrelid\n JOIN pg_am am ON i.relam = am.oid\n LEFT JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY k(attnum, ordinality) ON true\n LEFT JOIN pg_attribute a ON a.attnum = k.attnum AND a.attrelid = t.oid\n JOIN pg_namespace n ON t.relnamespace = n.oid\n WHERE\n n.nspname not like 'pg_%' and\n n.nspname <> 'information_schema'\n GROUP BY\n n.nspname, COALESCE(pt.parent_table::text, t.relname), i.relname, am.amname, ix.indisprimary, ix.indisunique\n ORDER BY\n COALESCE(pt.parent_table::text, t.relname), i.relname; -- @qd_introspection\n `);\n return indexes;\n }\n}\n\nexport type ColumnMetadata = {\n columnName: string;\n dataType: string;\n isNullable: boolean;\n stats: ColumnStats | null;\n};\n\ntype ColumnStats = {\n stainherit: boolean;\n stanullfrac: number;\n stawidth: number;\n stadistinct: number;\n stakind1: number;\n stakind2: number;\n stakind3: number;\n stakind4: number;\n stakind5: number;\n staop1: number;\n staop2: number;\n staop3: number;\n staop4: number;\n staop5: number;\n stacoll1: number;\n stacoll2: number;\n stacoll3: number;\n stacoll4: number;\n stacoll5: number;\n stanumbers1: number;\n stanumbers2: number;\n stanumbers3: number;\n stanumbers4: number;\n stanumbers5: number;\n};\n\nexport type TableMetadata = {\n tableName: string;\n schemaName: string;\n reltuples: number;\n relpages: number;\n relallvisible: number;\n relallfrozen?: number;\n columns: ColumnMetadata[];\n};\n\ntype TableName = string;\nexport type TableStats = {\n tupleEstimate: bigint;\n pageCount: number;\n};\n\nexport type SerializeResult = {\n schema: TableMetadata[];\n serialized: string;\n sampledRecords: Record<TableName, number>;\n};\n\nexport type IndexOrder = \"ASC\" | \"DESC\";\n\nexport type IndexedTable = {\n index_columns: Array<{ name: string; order: IndexOrder }>;\n is_primary: boolean;\n is_unique: boolean;\n index_name: string;\n // eslint-disable-next-line @typescript-eslint/ban-types\n index_type: \"btree\" | \"gin\" | (string & {});\n // this is always public\n schema_name: string;\n table_name: string;\n};\n","/**\n * Rewriter for pg_stat_statements queries.\n * Not all queries found in pg_stat_statements can be\n * directly sent back to the database without first being rewritten.\n */\nexport class PssRewriter {\n rewrite(query: string): string {\n return this.rewriteKeywordWithParameter(query);\n }\n\n private rewriteKeywordWithParameter(query: string): string {\n return query.replace(/\\b(\\w+) (\\$\\d+)\\b/gi, (match) => {\n const [keyword, parameter] = match.split(\" \");\n const isProblematicKeyword = this.problematicKeywords.includes(\n keyword.toLowerCase(),\n );\n if (!isProblematicKeyword) {\n return match;\n }\n return `(${parameter}::${keyword.toLowerCase()})`;\n });\n }\n\n private problematicKeywords = [\"interval\", \"timestamp\", \"geometry\"];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACMA,uBAMO;;;ACXP,4BAA4B;;;ACuB5B,SAAS,GACP,MACA,MAC2C;AAC3C,SAAO,QAAQ;AACjB;AAEA,SAAS,QAAQ,MAA6B;AAC5C,MAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C,WAAO;AAAA,EACT;AACA,QAAM,OAAO,OAAO,KAAK,IAAI;AAC7B,SAAO,KAAK,WAAW,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC;AACnD;AAMO,SAAS,YACd,MACA,OACS;AACT,QAAM,SAAkB,CAAC;AAEzB,MAAI,GAAG,MAAM,QAAQ,GAAG;AACtB,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,GAAG,MAAM,UAAU,GAAG;AAExB,UAAM,gBAAgB,MAAM,KAAK,CAAC,SAAS,SAAS,aAAa;AACjE,QAAI,iBAAiB,KAAK,SAAS,MAAM;AAEvC,YAAM,eAAe,kBAAkB,KAAK,SAAS,IAAI;AACzD,UAAI,cAAc;AAChB,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,UAAU;AAAA,UACV,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,MAAI,GAAG,MAAM,YAAY,GAAG;AAE1B,UAAM,aAAa,MAAM;AAAA,MACvB,CAAC,SACC,SAAS,oBACT,SAAS,aACT,SAAS;AAAA,IACb;AAEA,QAAI,CAAC,YAAY;AACf,YAAM,gBACJ,KAAK,WAAW,cAAc,KAAK,WAAW,WAAW,SAAS;AACpE,UAAI,eAAe;AAEjB,cAAM,kBAAkB,KAAK,WAAW,WAAY,KAAK,CAAC,aAAa;AACrE,iBACE,GAAG,UAAU,UAAU,KACtB,GAAG,UAAU,UAAU,KAAK,sBAAsB,QAAQ;AAAA,QAE/D,CAAC;AAED,YAAI,iBAAiB;AAEnB,cAAI,CAAC,KAAK,WAAW,aAAa;AAChC,mBAAO,KAAK;AAAA,cACV,MAAM;AAAA,cACN,UAAU;AAAA,cACV,SAAS;AAAA,YACX,CAAC;AAAA,UACH;AAGA,cAAI,CAAC,KAAK,WAAW,YAAY;AAC/B,mBAAO,KAAK;AAAA,cACV,MAAM;AAAA,cACN,UAAU;AAAA,cACV,SAAS;AAAA,YACX,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,GAAG,MAAM,QAAQ,GAAG;AACtB,UAAM,eACJ,KAAK,OAAO,SAAS,cACrB,KAAK,OAAO,QACZ,KAAK,OAAO,KAAK,SAAS,KAC1B,GAAG,KAAK,OAAO,KAAK,CAAC,GAAG,QAAQ,MAC/B,KAAK,OAAO,KAAK,CAAC,EAAE,OAAO,SAAS,OACnC,KAAK,OAAO,KAAK,CAAC,EAAE,OAAO,SAAS,QACpC,KAAK,OAAO,KAAK,CAAC,EAAE,OAAO,SAAS;AAExC,QAAI,cAAc;AAChB,YAAM,aAAa,eAAe,KAAK,OAAO,KAAK;AACnD,YAAM,cAAc,eAAe,KAAK,OAAO,KAAK;AAEpD,UAAI,cAAc,aAAa;AAC7B,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,UAAU;AAAA,UACV,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AAAA,IACF;AAGA,UAAM,WACJ,KAAK,OAAO,SAAS,gBAAgB,KAAK,OAAO,SAAS;AAE5D,QAAI,YAAY,KAAK,OAAO,OAAO;AACjC,YAAM,gBAAgB,uBAAuB,KAAK,OAAO,KAAK;AAC9D,UAAI,iBAAiB,cAAc,WAAW,GAAG,GAAG;AAClD,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,UAAU;AAAA,UACV,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,MAAI,GAAG,MAAM,YAAY,KAAK,KAAK,WAAW,gBAAgB;AAC5D,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,GAAG,MAAM,UAAU,GAAG;AAExB,QAAI,CAAC,KAAK,SAAS,OAAO;AACxB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MACE,GAAG,MAAM,YAAY,KACrB,KAAK,WAAW,cAChB,KAAK,WAAW,WAAW,SAAS,GACpC;AAEA,UAAM,aAAa,KAAK,WAAW,WAAW;AAAA,MAAO,CAAC,SACpD,GAAG,MAAM,UAAU;AAAA,IACrB,EAAE;AACF,QAAI,aAAa,GAAG;AAClB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,GAAG,MAAM,UAAU,KAAK,KAAK,SAAS,WAAW,WAAW;AAC9D,UAAM,UAAU,sBAAsB,IAAI;AAC1C,QAAI,WAAW,GAAG;AAChB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,GAAG,MAAM,QAAQ,GAAG;AACtB,QAAI,KAAK,OAAO,SAAS,YAAY;AACnC,UAAI;AACJ,UAAI,KAAK,OAAO,SAAS,GAAG,KAAK,OAAO,OAAO,MAAM,GAAG;AACtD,eAAO,KAAK,OAAO,MAAM;AAAA,MAC3B,WAAW,KAAK,OAAO,SAAS,GAAG,KAAK,OAAO,OAAO,MAAM,GAAG;AAC7D,eAAO,KAAK,OAAO,MAAM;AAAA,MAC3B;AAEA,UAAI,MAAM,SAAS,KAAK,MAAM,UAAU,IAAI;AAC1C,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,SACE;AAAA,UACF,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,kBAAkB,MAA0B;AAEnD,aAAW,OAAO,MAAM;AACtB,QAAI,mBAAmB,GAAG,GAAG;AAC3B,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,MAAwB;AAClD,MAAI,QAAQ,IAAI,KAAK,GAAG,MAAM,WAAW,GAAG;AAC1C,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO,KAAK,KAAK,CAAC,SAAS,mBAAmB,IAAI,CAAC;AAAA,EACrD;AAEA,MAAI,QAAQ,IAAI,GAAG;AACjB,UAAM,OAAO,OAAO,KAAK,IAAI;AAE7B,WAAO,mBAAmB,KAAK,KAAK,CAAC,CAAC,CAAC;AAAA,EACzC;AAEA,aAAW,SAAS,OAAO,OAAO,IAAI,GAAG;AACvC,QAAI,mBAAmB,KAAK,GAAG;AAC7B,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,sBACP,UACS;AAET,MAAI,SAAS,SAAS,QAAQ,GAAG,SAAS,SAAS,MAAM,UAAU,GAAG;AACpE,WAAO;AAAA,EACT;AACA,MAAI,SAAS,SAAS,QAAQ,GAAG,SAAS,SAAS,MAAM,UAAU,GAAG;AACpE,QAAI,sBAAsB,SAAS,SAAS,IAAI,GAAG;AACjD,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,SAAS,SAAS,QAAQ,GAAG,SAAS,SAAS,MAAM,UAAU,GAAG;AACpE,WAAO;AAAA,EACT;AACA,MAAI,SAAS,SAAS,QAAQ,GAAG,SAAS,SAAS,MAAM,UAAU,GAAG;AACpE,QAAI,sBAAsB,SAAS,SAAS,IAAI,GAAG;AACjD,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,eAAe,MAAwB;AAC9C,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,IAAI,KAAK,GAAG,MAAM,SAAS,GAAG;AAExC,WAAO,KAAK,QAAQ,WAAW;AAAA,EACjC;AAEA,SAAO;AACT;AAEA,SAAS,uBAAuB,MAA8B;AAC5D,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,IAAI,KAAK,GAAG,MAAM,SAAS,KAAK,KAAK,QAAQ,MAAM;AAC7D,WAAO,KAAK,QAAQ,KAAK,QAAQ;AAAA,EACnC;AAEA,SAAO;AACT;AAEA,SAAS,sBACP,MACQ;AACR,MAAI,KAAK,SAAS,WAAW,aAAa,CAAC,KAAK,SAAS,MAAM;AAC7D,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ;AACZ,aAAW,OAAO,KAAK,SAAS,MAAM;AACpC,QACE,QAAQ,GAAG,KACX,GAAG,KAAK,UAAU,KAClB,IAAI,SAAS,WAAW,WACxB;AACA,eAAS,sBAAsB,GAAG;AAAA,IACpC,OAAO;AACL,eAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AACT;;;AD1UO,IAAM,SAAN,MAAM,QAAO;AAAA,EAalB,YAA6B,OAAe;AAAf;AAZ7B,wBAAQ,iBAA+B,oBAAI,IAAI;AAC/C,wBAAQ,cAAa,oBAAI,IAAY;AACrC,wBAAQ,cAA0C,CAAC;AACnD,wBAAQ,wBAAuB,oBAAI,IAAY;AAC/C,wBAAQ,kBAA8C,CAAC;AACvD,wBAAQ,sBAAqB,oBAAI,IAAY;AAG7C;AAAA;AAAA,wBAAQ,kBAAiC,oBAAI,IAAI;AACjD,wBAAQ,mBAAyC,CAAC;AAClD,wBAAQ,UAAkB,CAAC;AAAA,EAEkB;AAAA,EAE7C,KAAK,MAAY;AAIf,SAAK,gBAAgB,oBAAI,IAAI;AAC7B,SAAK,aAAa,oBAAI,IAAY;AAClC,SAAK,aAAa,CAAC;AACnB,SAAK,uBAAuB,oBAAI,IAAY;AAC5C,SAAK,iBAAiB,CAAC;AACvB,SAAK,qBAAqB,oBAAI,IAAY;AAC1C,SAAK,iBAAiB,oBAAI,IAAI;AAC9B,SAAK,kBAAkB,CAAC;AACxB,SAAK,SAAS,CAAC;AAEf,YAAO,SAAS,MAAM,CAAC,GAAG,CAAC,MAAM,UAAU;AACzC,YAAM,aAAa,YAAY,MAAM,KAAK;AAC1C,WAAK,SAAS,CAAC,GAAG,KAAK,QAAQ,GAAG,UAAU;AAO5C,UAAIA,IAAG,MAAM,iBAAiB,GAAG;AAC/B,YAAI,KAAK,gBAAgB,SAAS;AAChC,eAAK,WAAW,IAAI,KAAK,gBAAgB,OAAO;AAAA,QAClD;AAAA,MACF;AAIA,UAAIA,IAAG,MAAM,gBAAgB,GAAG;AAC9B,YAAI,KAAK,eAAe,OAAO,WAAW;AACxC,eAAK,WAAW,IAAI,KAAK,eAAe,MAAM,SAAS;AAAA,QACzD;AAAA,MACF;AAGA,UAAIA,IAAG,MAAM,UAAU,GAAG;AACxB,YACE,KAAK,SAAS,OACd,KAAK,SAAS,gBACdA,IAAG,KAAK,SAAS,KAAK,WAAW,GACjC;AACA,eAAK,IAAI,KAAK,SAAS,KAAK;AAAA,YAC1B,OAAO,EAAE,UAAU,KAAK,SAAS,aAAa;AAAA,UAChD,CAAC;AAAA,QACH;AAAA,MACF;AAKA,UAAIA,IAAG,MAAM,UAAU,KAAK,KAAK,SAAS,SAAS;AACjD,cAAM,kBAAuC;AAAA,UAC3C,MAAM,KAAK,SAAS;AAAA,UACpB,OAAO,KAAK,SAAS;AAAA,UACrB,QAAQ;AAAA,QACV;AACA,YAAI,KAAK,SAAS,YAAY;AAC5B,0BAAgB,SAAS,KAAK,SAAS;AAAA,QACzC;AACA,aAAK,cAAc,IAAI,KAAK,SAAS,SAAS,eAAe;AAK7D,YAAI,KAAK,SAAS,OAAO,WAAW;AAClC,gBAAM,YAAY,KAAK,SAAS,MAAM;AACtC,gBAAM,kBAAkB,KAAK,cAAc,IAAI,SAAS;AACxD,gBAAM,OAA4B;AAAA,YAChC,MAAM,KAAK,SAAS;AAAA,YACpB,OAAO,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA,YAIrB,QAAQ;AAAA,YACR,OAAO;AAAA,UACT;AACA,cAAI,KAAK,SAAS,YAAY;AAC5B,iBAAK,SAAS,KAAK,SAAS;AAAA,UAC9B;AAeA,cAAI,iBAAiB;AACnB,kBAAM,kBACJ,KAAK,SAAS,SAAS,WAAW,KAAK,KAAK;AAC9C,gBAAI,CAAC,iBAAiB;AACpB,sBAAQ;AAAA,gBACN,kBAAkB,SAAS,0CAA0C,gBAAgB,IAAI;AAAA,cAC3F;AAAA,YACF;AAEA,iBAAK,gBAAgB,KAAK,IAAI;AAC9B;AAAA,UACF;AACA,eAAK,cAAc,IAAI,WAAW,IAAI;AAAA,QACxC;AAAA,MACF;AAGA,UAAIA,IAAG,MAAM,QAAQ,GAAG;AAItB,YAAI,KAAK,OAAO,QAAQA,IAAG,KAAK,OAAO,MAAM,WAAW,GAAG;AACzD,eAAK,IAAI,KAAK,OAAO,MAAM;AAAA,YACzB,MAAM;AAAA,cACJ,KAAK,KAAK,OAAO,cAAc;AAAA,cAC/B,OAAO,KAAK,OAAO,gBAAgB;AAAA,YACrC;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAGA,UAAIA,IAAG,MAAM,UAAU,KAAK,KAAK,SAAS,OAAO;AAC/C,YAAIA,IAAG,KAAK,SAAS,OAAO,QAAQ,GAAG;AACrC,cACE,KAAK,SAAS,MAAM,OAAO,SAC3BA,IAAG,KAAK,SAAS,MAAM,OAAO,OAAO,WAAW,GAChD;AACA,iBAAK,IAAI,KAAK,SAAS,MAAM,OAAO,KAAK;AAAA,UAC3C;AACA,cACE,KAAK,SAAS,MAAM,OAAO,SAC3BA,IAAG,KAAK,SAAS,MAAM,OAAO,OAAO,WAAW,GAChD;AACA,iBAAK,IAAI,KAAK,SAAS,MAAM,OAAO,KAAK;AAAA,UAC3C;AAAA,QACF;AAAA,MACF;AAEA,UAAIA,IAAG,MAAM,WAAW,GAAG;AAEzB,iBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,gBAAM,kBACJ,MAAM,CAAC,MAAM,mBACb,MAAM,IAAI,CAAC,MAAM,eACjB,MAAM,IAAI,CAAC,MAAM,SACjB,MAAM,IAAI,CAAC,MAAM;AACnB,cAAI,iBAAiB;AACnB,iBAAK,IAAI,MAAM,EAAE,SAAS,KAAK,CAAC;AAChC;AAAA,UACF;AACA;AAAA;AAAA,YAEE,MAAM,IAAI,CAAC,MAAM,gBACjB,MAAM,IAAI,CAAC,MAAM,eACjB,MAAM,IAAI,CAAC,MAAM,SACjB,MAAM,IAAI,CAAC,MAAM;AAAA,YACjB;AAEA,iBAAK,IAAI,MAAM,EAAE,SAAS,KAAK,CAAC;AAChC;AAAA,UACF;AAGA,cAAI,MAAM,CAAC,MAAM,cAAc,MAAM,IAAI,CAAC,MAAM,QAAQ;AAEtD,iBAAK,IAAI,MAAM,EAAE,SAAS,KAAK,CAAC;AAChC;AAAA,UACF;AAAA,QACF;AACA,aAAK,IAAI,IAAI;AAAA,MACf;AAAA,IACF,CAAC;AAED,WAAO;AAAA,MACL,YAAY,KAAK;AAAA,MACjB,sBAAsB,KAAK;AAAA,MAC3B,gBAAgB,KAAK;AAAA,MACrB,iBAAiB,KAAK;AAAA,MACtB,YAAY,KAAK;AAAA,MACjB,eAAe,KAAK;AAAA,MACpB,QAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAAA,EAEQ,IACN,MACA,SAKA;AACA,QAAI,CAAC,KAAK,UAAU,UAAU;AAC5B,cAAQ,MAAM,0CAA0C,IAAI;AAC5D;AAAA,IACF;AACA,QAAI,CAAC,KAAK,UAAU,QAAQ;AAC1B,cAAQ,MAAM,IAAI;AAClB,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AACA,QAAI,UAAU,SAAS,WAAW;AAClC,QAAI,gBAAwB,KAAK,UAAU;AAC3C,UAAM,QAA+B,KAAK,UAAU,OAAO;AAAA,MACzD,CAAC,OAAO,GAAG,WAAW;AACpB,YAAI,CAACA,IAAG,OAAO,QAAQ,KAAK,CAAC,MAAM,OAAO,MAAM;AAC9C,gBAAM,UAAM,mCAAY,KAAK;AAC7B,oBAAU;AACV,iBAAO;AAAA,YACL,QAAQ,IAAI,WAAW,GAAG;AAAA,YAC1B,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,QACF;AACA,cAAM,QAAQ;AACd,cAAM,OAAO,MAAM,OAAO,MAAM,UAAU;AAC1C,YAAI,SAAS;AACb,YAAI,KAAK,UAAU,aAAa,QAAW;AACzC,gBAAM,WAAW,KAAK,MAAM,aAAa;AACzC,cAAI,aAAa,KAAK;AACpB,qBAAS;AAAA,UACX;AAAA,QACF;AAEA,cAAM,kBAAkB,MAAM,OAAO,SAAS;AAC9C,yBAAiB,QAAQ,kBAAkB,IAAI,MAAM,SAAS,IAAI;AAClE,eAAO;AAAA,UACL,MAAM,MAAM,OAAO;AAAA,UACnB;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,UAAM,MAAM;AACZ,QAAI,KAAK,mBAAmB,IAAI,KAAK,UAAU,QAAQ,GAAG;AACxD;AAAA,IACF;AACA,SAAK,mBAAmB,IAAI,KAAK,UAAU,QAAQ;AACnD,UAAM,cAAc,GAAG,KAAK,MAAM,MAAM,KAAK,UAAU,UAAU,GAAG,CAAC;AACrE,UAAM,OAAO,KAAK,eAAe,IAAI,WAAW;AAChD,QAAI,CAAC,SAAS;AACZ,WAAK,eAAe,IAAI,cAAc,QAAQ,KAAK,CAAC;AAAA,IACtD;AACA,UAAM,MAAiC;AAAA,MACrC,WAAW,QAAQ;AAAA,MACnB,gBAAgB;AAAA,MAChB;AAAA,MACA,SAAS,WAAW;AAAA,MACpB,UAAU;AAAA,QACR,OAAO,KAAK,UAAU;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AACA,QAAI,SAAS,MAAM;AACjB,UAAI,OAAO,QAAQ;AAAA,IACrB;AACA,QAAI,SAAS,OAAO;AAClB,UAAI,QAAQ,QAAQ;AAAA,IACtB;AACA,SAAK,WAAW,KAAK,GAAG;AAAA,EAC1B;AAAA,EAEA,OAAO,SACL,MACA,OACA,UACA;AACA,QAAIC,SAAQ,IAAI,GAAG;AACjB,eAAS,MAAM,CAAC,GAAG,OAAO,YAAY,IAAI,CAAC,CAAC;AAAA,IAC9C;AACA,QAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,iBAAW,QAAQ,MAAM;AACvB,YAAIA,SAAQ,IAAI,GAAG;AACjB,kBAAO,SAAS,MAAM,OAAO,QAAQ;AAAA,QACvC;AAAA,MACF;AAAA,IACF,WAAWA,SAAQ,IAAI,GAAG;AACxB,YAAM,OAAO,OAAO,KAAK,IAAI;AAE7B,cAAO,SAAS,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,OAAO,YAAY,IAAI,CAAC,GAAG,QAAQ;AAAA,IACxE,OAAO;AACL,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,gBAAO,SAAS,OAAO,CAAC,GAAG,OAAO,GAAwB,GAAG,QAAQ;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AACF;AAYA,SAASD,IACP,MACA,MAC2C;AAC3C,SAAO,QAAQ;AACjB;AAEA,SAAS,YAAY,MAA+B;AAClD,QAAM,OAAO,OAAO,KAAK,IAAI;AAC7B,SAAO,KAAK,CAAC;AACf;AAEA,SAASC,SAAQ,MAA6B;AAC5C,MAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C,WAAO;AAAA,EACT;AACA,QAAM,OAAO,OAAO,KAAK,IAAI;AAC7B,SAAO,KAAK,WAAW,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC;AACnD;;;AD7UO,IAAM,oBAAoB;AAkF1B,IAAM,WAAN,MAAe;AAAA,EACpB,YAA6B,QAAgB;AAAhB;AAAA,EAAiB;AAAA,EAC9C,MAAM,QACJ,OACA,gBACyB;AACzB,UAAM,MAAO,MAAM,KAAK,OAAO,KAAK;AACpC,QAAI,CAAC,IAAI,OAAO;AACd,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,OAAO,IAAI,MAAM,CAAC,EAAE;AAC1B,QAAI,CAAC,MAAM;AACT,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,SAAS,IAAI,OAAO,KAAK;AAC/B,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI,OAAO,KAAK,IAAI;AACpB,UAAM,mBAAmB,WAAW;AAAA,MAClC,CAAC,GAAG,MAAM,EAAE,SAAS,MAAM,EAAE,SAAS;AAAA,IACxC;AACA,QAAI,YAAY;AAChB,eAAW,aAAa,kBAAkB;AAExC,YAAM,QAAQ,KAAK,oBAAoB,UAAU,OAAO,aAAa;AACrE,UAAI,MAAM,WAAW,GAAG;AACtB,gBAAQ,MAAM,SAAS;AACvB,cAAM,IAAI,MAAM,uCAAuC;AAAA,MACzD;AACA,UAAI;AACJ,UAAI,OAAO;AACX,UAAI,UAAU,SAAS;AACrB,gBAAQ,CAAC,UAAM,0BAAI,gCAAc,CAAC,CAAC;AACnC,eAAO;AAAA,MACT,WACE,MAAM,WAAW,KACjB,WAAW,IAAI,MAAM,CAAC,EAAE,IAAI;AAAA;AAAA,MAG5B,CAAC,cAAc,IAAI,MAAM,CAAC,EAAE,IAAI,GAChC;AACA,gBAAQ;AACR,eAAO;AAAA,MACT,OAAO;AACL,gBAAQ;AAAA,MACV;AACA,YAAM,YAAY,UAAU;AAC5B,YAAM,mBAAmB,UAAU,MAAM,GAAG,UAAU,SAAS,KAAK;AACpE,YAAM,kBAAkB,UAAU,MAAM,UAAU,SAAS,GAAG;AAC9D,kBAAY,GAAG,gBAAgB,GAAG,MAAM,SAAS,CAAC,GAAG,KAAK;AAAA,QACxD;AAAA,QACA;AAAA,MACF,CAAC;AACD,UAAI,qBAAqB,IAAI,SAAS,GAAG;AACvC,eAAO;AAAA,MACT;AACA,UAAI,CAAC,MAAM;AACT,uBAAe,KAAK,SAAS;AAC7B,6BAAqB,IAAI,SAAS;AAAA,MACpC;AAAA,IACF;AAEA,UAAM,mBAAqC,CAAC;AAC5C,eAAW,SAAS,cAAc,OAAO,GAAG;AAG1C,UAAI,CAAC,MAAM,OAAO;AAChB,yBAAiB,KAAK;AAAA,UACpB,QAAQ,MAAM;AAAA,UACd,OAAO,MAAM;AAAA,QACf,CAAC;AAAA,MACH;AAAA,IACF;AACA,UAAM,EAAE,MAAM,iBAAiB,IAAI,KAAK,oBAAoB,KAAK;AAEjE,UAAM,4BAA4B,iBAC9B,KAAK,oBAAoB,cAAc,EAAE,mBACzC;AAEJ,WAAO;AAAA,MACL;AAAA,MACA,sBAAsB;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,cACE,QACA,YACA,kBACsB;AAStB,UAAM,aAAmC,CAAC;AAC1C,UAAM,cAAc,oBAAI,IAAY;AACpC,aAAS,SAAS,OAA2B;AAC3C,YAAM,MAAM,IAAI,MAAM,MAAM,MAAM,MAAM,KAAK,MAAM,MAAM,MAAM;AAC/D,UAAI,YAAY,IAAI,GAAG,GAAG;AACxB;AAAA,MACF;AACA,kBAAY,IAAI,GAAG;AACnB,iBAAW,KAAK,KAAK;AAAA,IACvB;AACA,UAAM,iBAAiB,KAAK,iBAAiB,kBAAkB,MAAM;AACrE,eAAW,gBAAgB,YAAY;AACrC,YAAM,aAAa,aAAa,MAAM;AACtC,YAAM,sBAAsB,eAAe;AAC3C,YAAM,iBAAiB,eAAe;AACtC,YAAM,gBAAgB,eAAe;AACrC,UAAI,qBAAqB;AAEvB,cAAM,CAAC,MAAM,IAAI,aAAa;AAC9B,cAAM,mBAAmB,KAAK,UAAU,MAAM;AAC9C,mBAAW,SAAS,gBAAgB;AAClC,cAAI,CAAC,KAAK,UAAU,OAAO,gBAAgB,GAAG;AAC5C;AAAA,UACF;AACA,gBAAM,QAA4B;AAAA,YAChC,QAAQ,MAAM;AAAA,YACd,OAAO,MAAM;AAAA,YACb,QAAQ;AAAA,UACV;AACA,cAAI,aAAa,MAAM;AACrB,kBAAM,OAAO,aAAa;AAAA,UAC5B;AACA,cAAI,aAAa,OAAO;AACtB,kBAAM,QAAQ,aAAa;AAAA,UAC7B;AACA,mBAAS,KAAK;AAAA,QAChB;AAAA,MACF,WAAW,gBAAgB;AAEzB,cAAM,CAAC,OAAO,MAAM,IAAI,aAAa;AACrC,cAAM,kBAAkB,KAAK,UAAU,KAAK;AAC5C,cAAM,mBAAmB,KAAK,UAAU,MAAM;AAC9C,mBAAW,iBAAiB,gBAAgB;AAC1C,cAAI,CAAC,KAAK,UAAU,eAAe,gBAAgB,GAAG;AACpD;AAAA,UACF;AACA,gBAAM,QAA4B;AAAA,YAChC,QAAQ,cAAc;AAAA,YACtB,OAAO;AAAA,YACP,QAAQ;AAAA,UACV;AACA,cAAI,aAAa,MAAM;AACrB,kBAAM,OAAO,aAAa;AAAA,UAC5B;AACA,cAAI,aAAa,OAAO;AACtB,kBAAM,QAAQ,aAAa;AAAA,UAC7B;AACA,mBAAS,KAAK;AAAA,QAChB;AAAA,MACF,WAAW,eAAe;AAExB,cAAM,CAAC,QAAQ,OAAO,MAAM,IAAI,aAAa;AAC7C,cAAM,mBAAmB,KAAK,UAAU,MAAM;AAC9C,cAAM,kBAAkB,KAAK,UAAU,KAAK;AAC5C,cAAM,mBAAmB,KAAK,UAAU,MAAM;AAC9C,cAAM,QAA4B;AAAA,UAChC,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,QAAQ;AAAA,QACV;AACA,YAAI,aAAa,MAAM;AACrB,gBAAM,OAAO,aAAa;AAAA,QAC5B;AACA,YAAI,aAAa,OAAO;AACtB,gBAAM,QAAQ,aAAa;AAAA,QAC7B;AACA,iBAAS,KAAK;AAAA,MAChB,OAAO;AAEL,gBAAQ;AAAA,UACN;AAAA,UACA;AAAA,QACF;AACA;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,iBACN,kBACA,QACiB;AACjB,UAAM,iBAAkC,CAAC;AACzC,eAAW,mBAAmB,kBAAkB;AAC9C,YAAM,OAAO,OAAO,OAAO,CAAC,EAAE,WAAW,WAAW,MAAM;AAExD,YAAI,gBAAgB;AACpB,YAAI,gBAAgB,QAAQ;AAC1B,0BAAgB,eAAe,gBAAgB;AAAA,QACjD;AACA,eAAO,iBAAiB,cAAc,gBAAgB;AAAA,MACxD,CAAC;AACD,qBAAe,KAAK,GAAG,IAAI;AAAA,IAC7B;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,OAAsB,YAA6B;AACnE,WACE,MAAM,SAAS,KAAK,CAAC,WAAW,OAAO,eAAe,UAAU,KAAK;AAAA,EAEzE;AAAA,EAEQ,iBAAiB,OAAe,OAAc;AACpD,WAAO,MACJ;AAAA;AAAA,MAEC;AAAA,MACA,CAAC,GAAG,KAAK,KAAK,YAAY,UAAU;AAClC,eAAO,GAAG,GAAG,GAAG,MAAM,MAAM,GAAG,IAAI,EAAE,GACnC,QAAQ,WAAW,QAAQ,OAAO,MAAM,KAAK,CAAC,IAAI,EACpD;AAAA,MACF;AAAA,IACF,EACC,QAAQ,+BAA+B,CAAC,GAAG,KAAK,aAAa;AAC5D,aAAO,GAAG,GAAG,GAAG,MAAM,QAAQ,CAAC;AAAA,IACjC,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,oBACN,OACA,eACuB;AAIvB,QAAI,MAAM,WAAW,GAAG;AACtB,aAAO;AAAA,IACT;AACA,UAAM,YAAY,MAAM,CAAC;AACzB,UAAM,UAAU,cAAc,IAAI,UAAU,IAAI;AAChD,QAAI,SAAS;AACX,YAAM,CAAC,IAAI;AAAA,IACb;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,iBAA8C;AAC9D,WAAO,gBAAgB,SACnB,gBAAgB;AAAA;AAAA,MAEhB,gBAAgB,KAAK,YAAY;AAAA;AAAA,EACvC;AAAA,EAEQ,oBAAoB,OAAuC;AACjE,UAAM,eAAe,MAAM,QAAQ;AACnC,UAAM,gBAAgB,aAAa,YAAY,IAAI;AACnD,UAAM,cAAc,aAAa,YAAY,IAAI;AACjD,QAAI,kBAAkB,MAAM,gBAAgB,IAAI;AAC9C,aAAO,EAAE,MAAM,CAAC,GAAG,kBAAkB,aAAa;AAAA,IACpD;AACA,UAAM,mBAAmB,aAAa,MAAM,GAAG,aAAa;AAC5D,UAAM,YAAY,aAAa,MAAM,gBAAgB,GAAG,WAAW,EAAE,KAAK;AAC1E,QAAI,CAAC,aAAa,OAAO,cAAc,UAAU;AAC/C,aAAO,EAAE,MAAM,CAAC,GAAG,iBAAmC;AAAA,IACxD;AACA,UAAM,OAA0B,CAAC;AACjC,eAAW,SAAS,UAAU,MAAM,GAAG,GAAG;AACxC,YAAM,CAAC,KAAK,KAAK,IAAI,MAAM,MAAM,GAAG;AAIpC,UAAI,CAAC,OAAO,CAAC,OAAO;AAGlB,YAAI,KAAK,SAAS,GAAG;AACnB,kBAAQ;AAAA,YACN,6BAA6B,KAAK,gBAAgB,SAAS;AAAA,UAC7D;AAAA,QACF;AACA;AAAA,MACF;AACA,UAAI;AACF,YAAI,aAAa;AACjB,YAAI,MAAM,WAAW,GAAG,GAAG;AACzB,uBAAa;AAAA,QACf;AACA,YAAI,WAAW,MAAM;AACrB,YAAI,MAAM,SAAS,GAAG,GAAG;AACvB,sBAAY;AAAA,QACd;AAEA,cAAM,UAAU,mBAAmB,MAAM,MAAM,YAAY,QAAQ,CAAC;AAEpE,aAAK,KAAK,EAAE,KAAK,IAAI,KAAK,GAAG,OAAO,QAAQ,CAAC;AAAA,MAC/C,SAAS,KAAK;AAEZ,gBAAQ,MAAM,GAAG;AAAA,MACnB;AAAA,IACF;AACA,WAAO,EAAE,MAAM,iBAAiB;AAAA,EAClC;AACF;;;AG3aA,iBAAkB;AAGX,IAAM,kBAAkB,aAAE,OAAO,EAAE,MAAM,iBAAiB;AA0GjE,eAAsB,UACpB,IACA,OACkB;AAClB,MAAI;AACF,UAAM,GAAG,KAAK;AAAA;AAAA,6BAEW,KAAK;AAAA,KAC7B;AACD,WAAO;AAAA,EACT,SAAS,OAAO;AAEd,UAAM,GAAG,KAAK,sBAAsB;AACpC,WAAO;AAAA,EACT;AACF;;;AC1HO,SAAS,iBAAiB,OAAqB;AACpD,SAAO,MAAM,eAAe;AAC9B;AAMO,SAAS,yBAAyB,OAAqB;AAG5D,SAAO,CAAC,MAAM,cAAc,CAAC,MAAM;AACrC;;;ACPO,IAAM,uBAAN,MAAM,sBAAqB;AAAA,EAMhC,YAAoB,OAAe;AAAf;AALpB,wBAAiB,YAAmC,CAAC;AACrD,wBAAQ,mBAA2B;AACnC,wBAAQ,gBAAyB,CAAC;AAClC,wBAAQ,aAAY;AAAA,EAEgB;AAAA,EAEpC,IAAI,WAAmB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,OAAO,YAAY,YAAoB,MAAqB;AAC1D,QAAI,MAAM;AACR,aAAO,IAAI;AAAA,QACT,iBAAiB,IAAI,QAAQ,UAAU;AAAA,MACzC;AAAA,IACF;AACA,WAAO,IAAI,sBAAqB,mBAAmB,UAAU,GAAG;AAAA,EAClE;AAAA,EAEA,OAAO,SAAsC,QAAiB,MAAY;AACxE,UAAM,gBAAgB,UAAU,OAAO;AACvC,QAAI,OAAO;AACT,WAAK,SAAS,aAAa,IAAI;AAAA,IACjC,OAAO;AACL,WAAK,SAAS,aAAa,IAAI;AAAA,IACjC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,OAAqB;AAC7B,SAAK,QAAQ;AACb,WAAO;AAAA,EACT;AAAA,EAEA,aAAmB;AACjB,SAAK,kBAAkB;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,OAAuB;AAC7B,SAAK,eAAe;AACpB,WAAO;AAAA,EACT;AAAA,EAEA,QAAgB;AACd,QAAI,WAAW,KAAK,oBAAoB;AACxC,gBAAY,KAAK,gBAAgB,EAAE;AACnC,QAAI,KAAK,iBAAiB;AACxB,kBAAY;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,aAAa;AACX,UAAM,WAAW,KAAK,oBAAoB;AAC1C,UAAM,UAAU,KAAK,gBAAgB;AACrC,SAAK,YAAY,QAAQ;AACzB,QAAI,KAAK,iBAAiB;AACxB,cAAQ,SAAS;AAAA,IACnB;AACA,WAAO,EAAE,UAAU,OAAO,QAAQ,MAAM;AAAA,EAC1C;AAAA,EAEQ,sBAAsB;AAC5B,QAAI,WAAW;AACf,eAAW,OAAO,KAAK,UAAU;AAC/B,YAAM,QAAQ,KAAK,SAAS,GAAG;AAC/B,kBAAY,aAAa,GAAG,IAAI,KAAK;AAAA;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB;AACxB,QAAI,QAAQ;AACZ,QAAI,KAAK,aAAa,SAAS,GAAG;AAChC,eAAS,YAAY,KAAK,aAAa,KAAK,IAAI,CAAC;AAAA,IACnD;AACA,UAAM,YAAY,KAAK,MAAM,SAAS,GAAG,IAAI,KAAK;AAElD,UAAM,WAAW,MAAM;AACvB,aAAS,GAAG,KAAK,KAAK,GAAG,SAAS;AAClC,WAAO,EAAE,OAAO,SAAS;AAAA,EAC3B;AACF;;;ACxFO,IAAM,gBAAN,MAAM,cAAa;AAAA,EAChB,YACW,OACA,QACjB;AAFiB;AACA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMH,OAAO,WAAW,YAAkC;AAClD,UAAM,kBAAkB;AACxB,UAAM,QAAQ,WAAW,MAAM,UAAU;AACzC,QAAI,OAAO;AACT,aAAO,IAAI,cAAa,MAAM,CAAC,GAAG,IAAI;AAAA,IACxC;AACA,UAAM,SACJ,CAAC,gBAAgB,KAAK,UAAU,KAChC,KAAK,iBAAiB,IAAI,WAAW,YAAY,CAAC;AACpD,WAAO,IAAI,cAAa,YAAY,MAAM;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,aAAa,aAAsD;AACxE,WAAO,IAAI;AAAA,MACT,YACG,IAAI,CAAC,eAAe;AACnB,YAAI,OAAO,eAAe,UAAU;AAClC,iBAAO,cAAa,WAAW,UAAU;AAAA,QAC3C,OAAO;AACL,iBAAO;AAAA,QACT;AAAA,MACF,CAAC,EACA,KAAK,GAAG;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,WAAmB;AACjB,QAAI,KAAK,QAAQ;AACf,aAAO,IAAI,KAAK,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,IAC3C;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,SAAiB;AACf,WAAO,KAAK,SAAS;AAAA,EACvB;AA0KF;AAAA;AAAA;AAtKE,cAxDW,eAwDa,oBAAmB,oBAAI,IAAI;AAAA,EACjD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,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","names":["is","isANode","import_colorette","columns","import_colorette","import_zod","dedent"]}