UNPKG

@clickup/pg-microsharding

Version:
93 lines (83 loc) 2.22 kB
export type Value = string | number | null | Sql | Ident; /** * Quotes the value to be used as a shell argument. */ export function shellQuote(s: string): string { function quote(s: string): string { return "'" + s.replace(/'/g, "'\\''") + "'"; } return s === ";" ? "\\;" : s.match(/^[-a-z_0-9:/@.=,]+$/is) ? s : s.match(/^((?:--)?[-a-z_0-9]+=)(.*)$/is) ? RegExp.$1 + quote(RegExp.$2) : quote(s); } /** * Builds a compound SQL statement from a typed template string literal. All SQL * clauses built by this helper will have newlines removed. */ export function sql( strings: readonly string[], ...values: readonly Value[] ): Sql { const SEP = "\x00"; return new Sql( strings .join(SEP) .trim() .replace(/[ \t\r\n]+/g, " ") // glue all lines in one .trimEnd() .split(SEP), values, ); } /** * Builds a PG identifier from a string. */ export function ident(name: string): Ident { return new Ident(name); } /** * Joins multiple compound SQL statements. */ export function join( sqls: ReadonlyArray<Sql | Ident | string>, sep: string, ): Sql { return new Sql([sqls.map((sql) => sql.toString()).join(sep)], []); } class Ident { constructor(private name: string) {} toString(): string { return this.name.match(/^[a-z_][a-z_0-9]*$/is) ? this.name : '"' + this.name.replace(/"/g, '""') + '"'; } } export class Sql { constructor( private strings: readonly string[], private values: readonly Value[], ) {} toString(): string { return ( this.values .map( (value, i) => this.strings[i] + (value instanceof Sql || value instanceof Ident ? value.toString() : value === null || value === undefined ? "NULL" : // Postgres doesn't like ASCII NUL character (error message is "unterminated // quoted string" or "invalid message format"), so we remove it too. "'" + ("" + value).replace(/\0/g, "").replace(/'/g, "''") + "'"), ) .join("") + this.strings[this.values.length] ); } }