1/* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17enum EscapeFlag { 18 CaseInsensitive = 1, 19 MatchAny = 2, 20} 21 22function escape(s: string, flags?: number): string { 23 flags = flags === undefined ? 0 : flags; 24 // See https://www.sqlite.org/lang_expr.html#:~:text=A%20string%20constant 25 s = s.replace(/\'/g, '\'\''); 26 s = s.replace(/\[/g, '[[]'); 27 if (flags & EscapeFlag.CaseInsensitive) { 28 s = s.replace(/[a-zA-Z]/g, (m) => { 29 const lower = m.toLowerCase(); 30 const upper = m.toUpperCase(); 31 return `[${lower}${upper}]`; 32 }); 33 } 34 s = s.replace(/\?/g, '[?]'); 35 s = s.replace(/\*/g, '[*]'); 36 if (flags & EscapeFlag.MatchAny) { 37 s = `*${s}*`; 38 } 39 s = `'${s}'`; 40 return s; 41} 42 43export function escapeQuery(s: string): string { 44 return escape(s); 45} 46 47export function escapeSearchQuery(s: string): string { 48 return escape(s, EscapeFlag.CaseInsensitive | EscapeFlag.MatchAny); 49} 50 51export function escapeGlob(s: string): string { 52 // For globs we are only preoccupied by mismatching single quotes. 53 s = s.replace(/\'/g, '\'\''); 54 return `'*${s}*'`; 55} 56