/** * The query grammar this package can parse, the parse itself, and the three * renderings of a parsed query. * * WHAT THIS IS, AND WHAT IT IS NOT: a parser for a restricted controlled * language whose entire vocabulary is published from this file, in `PHRASES`. * It is not natural-language understanding, and the distinction is about * honesty rather than ambition. A translator that accepted arbitrary English * and emitted SQL anyway would have to guess, and a guess rendered as a query * is indistinguishable from a correct translation until it runs against the * caller's data -- by which time the caller has acted on it. So `parse` binds * what the grammar covers, reports every word it did not use in * `unrecognized`, and refuses outright when a piece the query needs is * missing, naming the piece. * * WHY THE PARSER OWNS IDENTIFIER VALIDATION: a value can be parameterized at * render time, but a table or column name cannot -- it has to be interpolated * into the statement. Every name a parse binds is checked here against * IDENTIFIER_PATTERN, so no renderer can be reached with an unchecked one. * The renderers quote identifiers and parameterize values; neither of them * sanitizes, because by then there is nothing left to sanitize. */ /** The intents the grammar can express. */ export declare const QUERY_INTENTS: readonly ["select", "count", "sum", "average", "min", "max"]; export type QueryIntent = (typeof QUERY_INTENTS)[number]; /** The intents that summarise a column rather than return rows. */ export declare const AGGREGATE_INTENTS: ReadonlyArray; /** The comparisons a filter can use. */ export declare const COMPARISONS: readonly ["=", "!=", ">", ">=", "<", "<=", "contains", "starts-with", "ends-with", "between"]; export type Comparison = (typeof COMPARISONS)[number]; export declare function isOrderedComparison(comparison: Comparison): boolean; export declare function isTextComparison(comparison: Comparison): boolean; /** How the parse decided which table or collection the query is over. */ export declare enum SourceBinding { /** Named by `from x` or `in x`. */ Keyword = "keyword", /** Taken from the position of the subject noun, with no keyword to confirm it. */ Position = "position" } /** The SQL dialects `renderSql` knows how to differ for. */ export declare enum SqlDialect { Ansi = "ansi", Postgres = "postgres", MySql = "mysql", Sqlite = "sqlite" } export declare const SQL_DIALECTS: ReadonlyArray; export declare function isSqlDialect(value: unknown): value is SqlDialect; /** The field types a caller-supplied schema can declare. */ export declare const FIELD_TYPES: readonly ["number", "string", "boolean", "date"]; export type FieldType = (typeof FIELD_TYPES)[number]; export declare function isFieldType(value: unknown): value is FieldType; /** A schema is sources, each naming its fields and their types. */ export type QuerySchema = Readonly>>>; export type QueryValue = string | number | boolean; export interface Filter { readonly field: string; readonly comparison: Comparison; readonly value: QueryValue; /** The upper operand, present only for `between`. */ readonly upper?: QueryValue; } export interface Ordering { readonly field: string; readonly descending: boolean; } export interface ParsedQuery { readonly intent: QueryIntent; readonly source: string; readonly sourceFrom: SourceBinding; /** The column an aggregate is over; null for `select` and `count`. */ readonly measure: string | null; /** The projected columns; empty means every column. */ readonly fields: readonly string[]; readonly filters: readonly Filter[]; readonly groupBy: readonly string[]; readonly ordering: Ordering | null; readonly limit: number | null; /** Every word the grammar read but did not use, in the order they appeared. */ readonly unrecognized: readonly string[]; /** * Every run of characters the tokenizer could not read at all, which is a * different thing from a word it read and did not use. Both are reported * because a query is acted on: a dropped `%` changes what a LIKE pattern * matches, and a dropped clause changes the answer. */ readonly unparsed: readonly string[]; } export declare function isIdentifier(value: string): boolean; declare enum LexKind { Intent = 0, Comparison = 1, From = 2, Where = 3, And = 4, Between = 5, OrderBy = 6, GroupBy = 7, By = 8, Of = 9, Direction = 10, Limit = 11, Noise = 12, Word = 13, Literal = 14 } /** * What a phrase binds to, before the text that matched it is attached. Every * lexeme carries its source text, so an unused one can be named back to the * caller instead of disappearing. */ type Seed = { readonly kind: LexKind.Intent; readonly intent: QueryIntent; } | { readonly kind: LexKind.Comparison; readonly comparison: Comparison; } | { readonly kind: LexKind.Direction; readonly descending: boolean; } | { readonly kind: LexKind.From | LexKind.Where | LexKind.And | LexKind.Between | LexKind.OrderBy | LexKind.GroupBy | LexKind.By | LexKind.Of | LexKind.Limit | LexKind.Noise | LexKind.Word; } | { readonly kind: LexKind.Literal; readonly value: QueryValue; readonly quoted: boolean; }; /** * THE PUBLISHED VOCABULARY. Everything the parser understands is in this * table; a word outside it is reported in `unrecognized` rather than guessed * at. Longer phrases are matched first, which is what lets `highest first` * mean a direction while `highest` alone means the max intent, and `is not` * mean inequality while `is` alone is the copula. */ export declare const PHRASES: ReadonlyArray; /** * Bind a query against the grammar, or refuse and name what is missing. */ export declare function parseQuery(query: string): ParsedQuery; export interface SqlRendering { readonly dialect: SqlDialect; readonly sql: string; readonly parameters: readonly QueryValue[]; /** The placeholder form this dialect uses, so the caller can bind by it. */ readonly placeholder: string; } export declare function renderSql(query: ParsedQuery, dialect?: SqlDialect): SqlRendering; export interface MongoRendering { readonly collection: string; readonly method: 'find' | 'countDocuments' | 'aggregate'; readonly filter: Record; readonly projection?: Record; readonly sort?: Record; readonly limit?: number; readonly pipeline?: ReadonlyArray>; } export declare function renderMongo(query: ParsedQuery): MongoRendering; export interface GraphqlRendering { readonly query: string; readonly variables: Readonly>; /** * GraphQL has no standard filter or pagination convention, so the renderer * names the one it emits rather than implying there is only one. */ readonly convention: 'field-suffix-filters'; } export declare function renderGraphql(query: ParsedQuery): GraphqlRendering; /** Render a parsed query back into the grammar, which is what `suggest-query` emits. */ export declare function renderGrammar(query: ParsedQuery): string; export {}; //# sourceMappingURL=query-core.d.ts.map