Attempt to give more documents scope objects. bug 440275, r+sr=jst
[mozilla-central.git] / storage / src / mozStorageUnicodeFunctions.cpp
blob8d3ad8172268a694f08a3da4e876ab0c99241527
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 sts=2
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is unicode functions code.
18 * The Initial Developer of the Original Code is
19 * Mozilla Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 2007
21 * the Initial Developer. All Rights Reserved.
23 * This code is based off of icu.c from the sqlite code
24 * whose original author is danielk1977
26 * Contributor(s):
27 * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
29 * Alternatively, the contents of this file may be used under the terms of
30 * either the GNU General Public License Version 2 or later (the "GPL"), or
31 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
43 #include "mozStorageUnicodeFunctions.h"
44 #include "nsUnicharUtils.h"
46 int
47 StorageUnicodeFunctions::RegisterFunctions(sqlite3 *aDB)
49 struct Functions {
50 const char *zName;
51 int nArg;
52 int enc;
53 void *pContext;
54 void (*xFunc)(sqlite3_context*, int, sqlite3_value**);
55 } functions[] = {
56 {"lower", 1, SQLITE_UTF16, 0, caseFunction},
57 {"lower", 1, SQLITE_UTF8, 0, caseFunction},
58 {"upper", 1, SQLITE_UTF16, (void*)1, caseFunction},
59 {"upper", 1, SQLITE_UTF8, (void*)1, caseFunction},
61 {"like", 2, SQLITE_UTF16, 0, likeFunction},
62 {"like", 2, SQLITE_UTF8, 0, likeFunction},
63 {"like", 3, SQLITE_UTF16, 0, likeFunction},
64 {"like", 3, SQLITE_UTF8, 0, likeFunction},
67 int rv = SQLITE_OK;
68 for (unsigned i = 0; SQLITE_OK == rv && i < NS_ARRAY_LENGTH(functions); ++i) {
69 struct Functions *p = &functions[i];
70 rv = sqlite3_create_function(aDB, p->zName, p->nArg, p->enc, p->pContext,
71 p->xFunc, NULL, NULL);
74 return rv;
77 void
78 StorageUnicodeFunctions::caseFunction(sqlite3_context *p,
79 int aArgc,
80 sqlite3_value **aArgv)
82 NS_ASSERTION(1 == aArgc, "Invalid number of arguments!");
84 nsAutoString data(static_cast<const PRUnichar *>(sqlite3_value_text16(aArgv[0])));
85 PRBool toUpper = sqlite3_user_data(p) ? PR_TRUE : PR_FALSE;
87 if (toUpper)
88 ToUpperCase(data);
89 else
90 ToLowerCase(data);
92 // Give sqlite our result
93 sqlite3_result_text16(p, data.get(), -1, SQLITE_TRANSIENT);
96 static int
97 likeCompare(nsAString::const_iterator aPatternItr,
98 nsAString::const_iterator aPatternEnd,
99 nsAString::const_iterator aStringItr,
100 nsAString::const_iterator aStringEnd,
101 PRUnichar aEscape)
103 const PRUnichar MATCH_ALL('%');
104 const PRUnichar MATCH_ONE('_');
106 PRBool lastWasEscape = PR_FALSE;
107 while (aPatternItr != aPatternEnd) {
109 * What we do in here is take a look at each character from the input
110 * pattern, and do something with it. There are 4 possibilities:
111 * 1) character is an un-escaped match-all character
112 * 2) character is an un-escaped match-one character
113 * 3) character is an un-escaped escape character
114 * 4) character is not any of the above
116 if (!lastWasEscape && *aPatternItr == MATCH_ALL) {
117 // CASE 1
119 * Now we need to skip any MATCH_ALL or MATCH_ONE characters that follow a
120 * MATCH_ALL character. For each MATCH_ONE character, skip one character
121 * in the pattern string.
123 while (*aPatternItr == MATCH_ALL || *aPatternItr == MATCH_ONE) {
124 if (*aPatternItr == MATCH_ONE) {
125 // If we've hit the end of the string we are testing, no match
126 if (aStringItr == aStringEnd)
127 return 0;
128 aStringItr++;
130 aPatternItr++;
133 // If we've hit the end of the pattern string, match
134 if (aPatternItr == aPatternEnd)
135 return 1;
137 while (aStringItr != aStringEnd) {
138 if (likeCompare(aPatternItr, aPatternEnd, aStringItr, aStringEnd, aEscape)) {
139 // we've hit a match, so indicate this
140 return 1;
142 aStringItr++;
145 // No match
146 return 0;
147 } else if (!lastWasEscape && *aPatternItr == MATCH_ONE) {
148 // CASE 2
149 if (aStringItr == aStringEnd) {
150 // If we've hit the end of the string we are testing, no match
151 return 0;
153 aStringItr++;
154 lastWasEscape = PR_FALSE;
155 } else if (!lastWasEscape && *aPatternItr == aEscape) {
156 // CASE 3
157 lastWasEscape = PR_TRUE;
158 } else {
159 // CASE 4
160 if (ToUpperCase(*aStringItr) != ToUpperCase(*aPatternItr)) {
161 // If we've hit a point where the strings don't match, there is no match
162 return 0;
164 aStringItr++;
165 lastWasEscape = PR_FALSE;
168 aPatternItr++;
171 return aStringItr == aStringEnd;
175 * This implements the like() SQL function. This is used by the LIKE operator.
176 * The SQL statement 'A LIKE B' is implemented as 'like(B, A)', and if there is
177 * an escape character, say E, it is implemented as 'like(B, A, E)'.
179 void
180 StorageUnicodeFunctions::likeFunction(sqlite3_context *p,
181 int aArgc,
182 sqlite3_value **aArgv)
184 NS_ASSERTION(2 == aArgc || 3 == aArgc, "Invalid number of arguments!");
186 if (sqlite3_value_bytes(aArgv[0]) > SQLITE_MAX_LIKE_PATTERN_LENGTH) {
187 sqlite3_result_error(p, "LIKE or GLOB pattern too complex", SQLITE_TOOBIG);
188 return;
191 if (!sqlite3_value_text16(aArgv[0]) || !sqlite3_value_text16(aArgv[1]))
192 return;
194 nsDependentString A(static_cast<const PRUnichar *>(sqlite3_value_text16(aArgv[1])));
195 nsDependentString B(static_cast<const PRUnichar *>(sqlite3_value_text16(aArgv[0])));
196 NS_ASSERTION(!B.IsEmpty(), "LIKE string must not be null!");
198 PRUnichar E = 0;
199 if (3 == aArgc)
200 E = static_cast<const PRUnichar *>(sqlite3_value_text16(aArgv[2]))[0];
202 nsAString::const_iterator itrString, endString;
203 A.BeginReading(itrString);
204 A.EndReading(endString);
205 nsAString::const_iterator itrPattern, endPattern;
206 B.BeginReading(itrPattern);
207 B.EndReading(endPattern);
208 sqlite3_result_int(p, likeCompare(itrPattern, endPattern,
209 itrString, endString, E));