Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/util/db-schema/operators.ts
Views: 687
/*1TODO: Postgres supports regular expressions and SIMILAR to:2https://www.postgresql.org/docs/current/functions-matching.html3However, there are significant performance implications4to using those. Maybe restrict use of regexp to admins only?5*/6import LRU from "lru-cache";7import { unreachable } from "../misc";89// ORDER MATTERS! -- this gets looped over and searches happen -- so10// the 1-character ops must be after 2-character ops that contain them.11// This is ONLY used by the database (and for interaction with it).12export const OPERATORS = [13"!=",14"<>",15"<=",16">=",17"==",18"<",19">",20"=",21"IS NOT",22"IS",23"ILIKE",24"LIKE",25"NOT ILIKE",26"NOT LIKE",27"ANY", // only array28"MINLEN", // only array29"MAXLEN", // only array30] as const;31export type Operator = (typeof OPERATORS)[number];3233export function isToOperand(operand: string) {34switch (`${operand}`.toLowerCase()) {35case "null":36case "undefined":37return "null";38case "unknown":39return "unknown";40case "true":41return "true";42case "false":43return "false";44default:45return "true";46}47}4849export function opToFunction(op: Operator): (a, b) => boolean {50switch (op) {51case "=":52case "==":53return (a, b) => a === b;54case "!=":55case "<>":56return (a, b) => a !== b;57case "<=":58return (a, b) => a <= b;59case ">=":60return (a, b) => a >= b;61case "<":62return (a, b) => a < b;63case ">":64return (a, b) => a > b;65case "IS":66return (a, b) => {67// see https://www.postgresql.org/docs/current/functions-comparison.html68switch (`${b}`.toLowerCase()) {69case "null":70case "undefined":71case "unknown":72return a == null;73case "true":74return !!a;75case "false":76return !a;77default: // shouldn't happen78return false;79}80};81case "IS NOT": {82const f = opToFunction("IS");83return (a, b) => !f(a, b);84}85case "LIKE":86return (a, b) => {87const re = likeRegExp(b);88return `${a}`.match(re) != null;89};90case "NOT LIKE": {91const f = opToFunction("LIKE");92return (a, b) => !f(a, b);93}94case "ILIKE":95return (a, b) => {96const re = likeRegExp(b, true);97return `${a}`.match(re) != null;98};99case "NOT ILIKE": {100const f = opToFunction("ILIKE");101return (a, b) => !f(a, b);102}103case "ANY":104return (a, b) => {105if (!Array.isArray(b)) {106return false;107}108return b.includes(a);109};110case "MINLEN":111// array b has at least a entries112return (a, b) => {113if (!Array.isArray(b)) {114return false;115}116return b.length >= a;117};118case "MAXLEN":119// array b has at least a entries120return (a, b) => {121if (!Array.isArray(b)) {122return false;123}124return b.length <= a;125};126default:127unreachable(op);128throw Error(`operator must be one of '${JSON.stringify(OPERATORS)}'`);129}130}131132// This is from133// https://stackoverflow.com/questions/1314045/emulating-sql-like-in-javascript134135const likeRegExpCache = new LRU<string, RegExp>({ max: 100 });136137function likeRegExp(expression: string, caseInsensitive?: boolean): RegExp {138const key = expression + `${caseInsensitive}`;139if (likeRegExpCache.has(key)) {140return likeRegExpCache.get(key) as RegExp;141}142const re = new RegExp(143`^${expression144.split(/(\[.+?\])/g)145.map((s, i) =>146i % 2147? s.replace(/\\/g, "\\\\")148: s.replace(/[-\/\\^$*+?.()|[\]{}%_]/g, (m) => {149switch (m) {150case "%":151return ".*";152case "_":153return ".";154default:155return `\\${m}`;156}157}),158)159.join("")}$`,160caseInsensitive ? "i" : "",161);162likeRegExpCache.set(key, re);163return re;164}165166167