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