[patches] [PATCH v2 1/2] sql: make upper lower work over ICU (defult locale)
AKhatskevich
avkhatskevich at tarantool.org
Tue Feb 6 15:53:13 MSK 2018
Add support of unicode characters to SQL internal functions `lower` and
`upper`.
This functions work in default locale since now and it is impossible to
change the default locale without recompilation. However it is enough
for most cases.
Closes #2654
---
src/box/sql/func.c | 100 ++++++++++++-----------
test/sql/icu-upper-lower.result | 165 ++++++++++++++++++++++++++++++++++++++
test/sql/icu-upper-lower.test.lua | 84 +++++++++++++++++++
3 files changed, 303 insertions(+), 46 deletions(-)
create mode 100644 test/sql/icu-upper-lower.result
create mode 100644 test/sql/icu-upper-lower.test.lua
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 586a13504..fa4923403 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -35,9 +35,9 @@
* time functions, are implemented separately.)
*/
#include "sqliteInt.h"
-#include <stdlib.h>
-#include <assert.h>
#include "vdbeInt.h"
+#include <unicode/ustring.h>
+#include <unicode/ucasemap.h>
/*
* Return the collating function associated with a function.
@@ -476,49 +476,49 @@ contextMalloc(sqlite3_context * context, i64 nByte)
/*
* Implementation of the upper() and lower() SQL functions.
*/
-static void
-upperFunc(sqlite3_context * context, int argc, sqlite3_value ** argv)
-{
- char *z1;
- const char *z2;
- int i, n;
- UNUSED_PARAMETER(argc);
- z2 = (char *)sqlite3_value_text(argv[0]);
- n = sqlite3_value_bytes(argv[0]);
- /* Verify that the call to _bytes() does not invalidate the _text() pointer */
- assert(z2 == (char *)sqlite3_value_text(argv[0]));
- if (z2) {
- z1 = contextMalloc(context, ((i64) n) + 1);
- if (z1) {
- for (i = 0; i < n; i++) {
- z1[i] = (char)sqlite3Toupper(z2[i]);
- }
- sqlite3_result_text(context, z1, n, sqlite3_free);
- }
- }
-}
-static void
-lowerFunc(sqlite3_context * context, int argc, sqlite3_value ** argv)
-{
- char *z1;
- const char *z2;
- int i, n;
- UNUSED_PARAMETER(argc);
- z2 = (char *)sqlite3_value_text(argv[0]);
- n = sqlite3_value_bytes(argv[0]);
- /* Verify that the call to _bytes() does not invalidate the _text() pointer */
- assert(z2 == (char *)sqlite3_value_text(argv[0]));
- if (z2) {
- z1 = contextMalloc(context, ((i64) n) + 1);
- if (z1) {
- for (i = 0; i < n; i++) {
- z1[i] = sqlite3Tolower(z2[i]);
- }
- sqlite3_result_text(context, z1, n, sqlite3_free);
- }
- }
-}
+static UCaseMap *pUCaseMap;
+
+#define ICU_CASE_CONVERT(case_type) \
+static void \
+case_type##ICUFunc(sqlite3_context *context, int argc, sqlite3_value **argv) \
+{ \
+ char *z1; \
+ const char *z2; \
+ int n; \
+ UNUSED_PARAMETER(argc); \
+ z2 = (char *)sqlite3_value_text(argv[0]); \
+ n = sqlite3_value_bytes(argv[0]); \
+ /* \
+ * Verify that the call to _bytes() \
+ * does not invalidate the _text() pointer. \
+ */ \
+ assert(z2 == (char *)sqlite3_value_text(argv[0])); \
+ if (!z2) \
+ return; \
+ z1 = contextMalloc(context, ((i64) n) + 1); \
+ if (!z1) { \
+ sqlite3_result_error_nomem(context); \
+ return; \
+ } \
+ UErrorCode status = U_ZERO_ERROR; \
+ int len = ucasemap_utf8To##case_type(pUCaseMap, z1, n, z2, n, &status);\
+ if (len > n) { \
+ status = U_ZERO_ERROR; \
+ sqlite3_free(z1); \
+ z1 = contextMalloc(context, ((i64) len) + 1); \
+ if (!z1) { \
+ sqlite3_result_error_nomem(context); \
+ return; \
+ } \
+ ucasemap_utf8To##case_type(pUCaseMap, z1, len, z2, n, &status);\
+ } \
+ sqlite3_result_text(context, z1, len, sqlite3_free); \
+} \
+
+ICU_CASE_CONVERT(Lower);
+ICU_CASE_CONVERT(Upper);
+
/*
* Some functions like COALESCE() and IFNULL() and UNLIKELY() are implemented
@@ -1831,6 +1831,14 @@ sqlite3IsLikeFunction(sqlite3 * db, Expr * pExpr, int *pIsNocase, char *aWc)
void
sqlite3RegisterBuiltinFunctions(void)
{
+ /*
+ * Initialize default case map for UPPER/LOWER functions
+ * This structure is not freed at db exit, but that is ok.
+ */
+ UErrorCode status = U_ZERO_ERROR;
+
+ pUCaseMap = ucasemap_open(NULL, 0, &status);
+ assert(pUCaseMap);
/*
* The following array holds FuncDef structures for all of the functions
* defined in this file.
@@ -1876,8 +1884,8 @@ sqlite3RegisterBuiltinFunctions(void)
FUNCTION(round, 1, 0, 0, roundFunc),
FUNCTION(round, 2, 0, 0, roundFunc),
#endif
- FUNCTION(upper, 1, 0, 0, upperFunc),
- FUNCTION(lower, 1, 0, 0, lowerFunc),
+ FUNCTION(upper, 1, 0, 0, UpperICUFunc),
+ FUNCTION(lower, 1, 0, 0, LowerICUFunc),
FUNCTION(hex, 1, 0, 0, hexFunc),
FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
VFUNCTION(random, 0, 0, 0, randomFunc),
diff --git a/test/sql/icu-upper-lower.result b/test/sql/icu-upper-lower.result
new file mode 100644
index 000000000..9989ed239
--- /dev/null
+++ b/test/sql/icu-upper-lower.result
@@ -0,0 +1,165 @@
+test_run = require('test_run').new()
+---
+...
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+upper_lower_test = function (str)
+ return box.sql.execute(string.format("select lower('%s'), upper('%s')", str, str))
+end;
+---
+...
+-- Some pangrams
+-- Azerbaijanian
+upper_lower_test([[
+ Zəfər, jaketini də, papağını da götür, bu axşam hava çox soyuq olacaq.
+]]);
+---
+- - [' zəfər, jaketini də, papağını da götür, bu axşam hava çox soyuq olacaq. ',
+ ' ZƏFƏR, JAKETINI DƏ, PAPAĞINI DA GÖTÜR, BU AXŞAM HAVA ÇOX SOYUQ OLACAQ. ']
+...
+upper_lower_test([[
+ The quick brown fox jumps over the lazy dog.
+]]);
+---
+- - [' the quick brown fox jumps over the lazy dog. ', ' THE QUICK BROWN FOX
+ JUMPS OVER THE LAZY DOG. ']
+...
+-- English
+upper_lower_test([[
+ The quick brown fox jumps over the lazy dog.
+]]);
+---
+- - [' the quick brown fox jumps over the lazy dog. ', ' THE QUICK BROWN FOX
+ JUMPS OVER THE LAZY DOG. ']
+...
+-- Armenian
+upper_lower_test([[
+ Բել դղյակի ձախ ժամն օֆ ազգությանը ցպահանջ չճշտած վնաս էր եւ փառք
+]]);
+---
+- - [' բել դղյակի ձախ ժամն օֆ ազգությանը ցպահանջ չճշտած վնաս էր եւ փառք ', ' ԲԵԼ
+ ԴՂՅԱԿԻ ՁԱԽ ԺԱՄՆ ՕՖ ԱԶԳՈՒԹՅԱՆԸ ՑՊԱՀԱՆՋ ՉՃՇՏԱԾ ՎՆԱՍ ԷՐ ԵՒ ՓԱՌՔ ']
+...
+-- Belarussian
+upper_lower_test([[
+ У Іўі худы жвавы чорт у зялёнай камізэльцы пабег пад’есці фаршу з юшкай
+]]);
+---
+- - [' у іўі худы жвавы чорт у зялёнай камізэльцы пабег пад’есці фаршу з юшкай ',
+ ' У ІЎІ ХУДЫ ЖВАВЫ ЧОРТ У ЗЯЛЁНАЙ КАМІЗЭЛЬЦЫ ПАБЕГ ПАД’ЕСЦІ ФАРШУ З ЮШКАЙ ']
+...
+-- Greek
+upper_lower_test([[
+ Τάχιστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός
+]]);
+---
+- - [' τάχιστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός ', ' ΤΆΧΙΣΤΗ
+ ΑΛΏΠΗΞ ΒΑΦΉΣ ΨΗΜΈΝΗ ΓΗ, ΔΡΑΣΚΕΛΊΖΕΙ ΥΠΈΡ ΝΩΘΡΟΎ ΚΥΝΌΣ ']
+...
+-- Irish
+upper_lower_test([[
+ Chuaigh bé mhórshách le dlúthspád fíorfhinn trí hata mo dhea-phorcáin bhig
+]]);
+---
+- - [' chuaigh bé mhórshách le dlúthspád fíorfhinn trí hata mo dhea-phorcáin bhig ',
+ ' CHUAIGH BÉ MHÓRSHÁCH LE DLÚTHSPÁD FÍORFHINN TRÍ HATA MO DHEA-PHORCÁIN BHIG ']
+...
+-- Spain
+upper_lower_test([[
+ Quiere la boca exhausta vid, kiwi, piña y fugaz jamón
+]]);
+---
+- - [' quiere la boca exhausta vid, kiwi, piña y fugaz jamón ', ' QUIERE LA
+ BOCA EXHAUSTA VID, KIWI, PIÑA Y FUGAZ JAMÓN ']
+...
+-- Korean
+upper_lower_test([[
+ 키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다
+]]);
+---
+- - [' 키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다 ', ' 키스의 고유조건은 입술끼리 만나야 하고 특별한
+ 기술은 필요치 않다 ']
+...
+-- Latvian
+upper_lower_test([[
+ Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus
+]]);
+---
+- - [' glāžšķūņa rūķīši dzērumā čiepj baha koncertflīģeļu vākus ', ' GLĀŽŠĶŪŅA
+ RŪĶĪŠI DZĒRUMĀ ČIEPJ BAHA KONCERTFLĪĢEĻU VĀKUS ']
+...
+-- German
+upper_lower_test([[
+ Zwölf große Boxkämpfer jagen Viktor quer über den Sylter Deich
+]]);
+---
+- - [' zwölf große boxkämpfer jagen viktor quer über den sylter deich ', ' ZWÖLF
+ GROSSE BOXKÄMPFER JAGEN VIKTOR QUER ÜBER DEN SYLTER DEICH ']
+...
+-- Polish
+upper_lower_test([[
+ Pchnąć w tę łódź jeża lub ośm skrzyń fig.
+]]);
+---
+- - [' pchnąć w tę łódź jeża lub ośm skrzyń fig. ', ' PCHNĄĆ W TĘ ŁÓDŹ JEŻA
+ LUB OŚM SKRZYŃ FIG. ']
+...
+-- Ukrainian
+upper_lower_test([[
+ Чуєш їх, доцю, га? Кумедна ж ти, прощайся без ґольфів!
+]]);
+---
+- - [' чуєш їх, доцю, га? кумедна ж ти, прощайся без ґольфів! ', ' ЧУЄШ ЇХ,
+ ДОЦЮ, ГА? КУМЕДНА Ж ТИ, ПРОЩАЙСЯ БЕЗ ҐОЛЬФІВ! ']
+...
+-- Czech
+upper_lower_test([[
+ Příliš žluťoučký kůň úpěl ďábelské ódy
+]]);
+---
+- - [' příliš žluťoučký kůň úpěl ďábelské ódy ', ' PŘÍLIŠ ŽLUŤOUČKÝ KŮŇ ÚPĚL
+ ĎÁBELSKÉ ÓDY ']
+...
+-- Esperanto
+upper_lower_test([[
+ Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj
+]]);
+---
+- - [' laŭ ludoviko zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj ', ' LAŬ
+ LUDOVIKO ZAMENHOF BONGUSTAS FREŜA ĈEĤA MANĜAĴO KUN SPICOJ ']
+...
+-- Japanese
+upper_lower_test([[
+ いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす
+]]);
+---
+- - [' いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす ', ' いろはにほへと
+ ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす ']
+...
+-- Turkish
+upper_lower_test([[
+ Pijamalı hasta yağız şoföre çabucak güvendi. EXTRA: İ
+]]);
+---
+- - [' pijamalı hasta yağız şoföre çabucak güvendi. extra: i̇ ', ' PIJAMALI
+ HASTA YAĞIZ ŞOFÖRE ÇABUCAK GÜVENDI. EXTRA: İ ']
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
+-- Bad test cases
+box.sql.execute("select upper('1', 2)")
+---
+- error: wrong number of arguments to function UPPER()
+...
+box.sql.execute("select upper(\"1\")")
+---
+- error: 'no such column: 1'
+...
+box.sql.execute("select upper()")
+---
+- error: wrong number of arguments to function UPPER()
+...
diff --git a/test/sql/icu-upper-lower.test.lua b/test/sql/icu-upper-lower.test.lua
new file mode 100644
index 000000000..bb1a5189b
--- /dev/null
+++ b/test/sql/icu-upper-lower.test.lua
@@ -0,0 +1,84 @@
+test_run = require('test_run').new()
+
+test_run:cmd("setopt delimiter ';'")
+
+upper_lower_test = function (str)
+ return box.sql.execute(string.format("select lower('%s'), upper('%s')", str, str))
+end;
+
+-- Some pangrams
+-- Azerbaijanian
+upper_lower_test([[
+ Zəfər, jaketini də, papağını da götür, bu axşam hava çox soyuq olacaq.
+]]);
+upper_lower_test([[
+ The quick brown fox jumps over the lazy dog.
+]]);
+-- English
+upper_lower_test([[
+ The quick brown fox jumps over the lazy dog.
+]]);
+-- Armenian
+upper_lower_test([[
+ Բել դղյակի ձախ ժամն օֆ ազգությանը ցպահանջ չճշտած վնաս էր եւ փառք
+]]);
+-- Belarussian
+upper_lower_test([[
+ У Іўі худы жвавы чорт у зялёнай камізэльцы пабег пад’есці фаршу з юшкай
+]]);
+-- Greek
+upper_lower_test([[
+ Τάχιστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός
+]]);
+-- Irish
+upper_lower_test([[
+ Chuaigh bé mhórshách le dlúthspád fíorfhinn trí hata mo dhea-phorcáin bhig
+]]);
+-- Spain
+upper_lower_test([[
+ Quiere la boca exhausta vid, kiwi, piña y fugaz jamón
+]]);
+-- Korean
+upper_lower_test([[
+ 키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다
+]]);
+-- Latvian
+upper_lower_test([[
+ Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus
+]]);
+-- German
+upper_lower_test([[
+ Zwölf große Boxkämpfer jagen Viktor quer über den Sylter Deich
+]]);
+-- Polish
+upper_lower_test([[
+ Pchnąć w tę łódź jeża lub ośm skrzyń fig.
+]]);
+-- Ukrainian
+upper_lower_test([[
+ Чуєш їх, доцю, га? Кумедна ж ти, прощайся без ґольфів!
+]]);
+-- Czech
+upper_lower_test([[
+ Příliš žluťoučký kůň úpěl ďábelské ódy
+]]);
+-- Esperanto
+upper_lower_test([[
+ Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj
+]]);
+-- Japanese
+upper_lower_test([[
+ いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす
+]]);
+-- Turkish
+upper_lower_test([[
+ Pijamalı hasta yağız şoföre çabucak güvendi. EXTRA: İ
+]]);
+
+test_run:cmd("setopt delimiter ''");
+
+-- Bad test cases
+box.sql.execute("select upper('1', 2)")
+box.sql.execute("select upper(\"1\")")
+box.sql.execute("select upper()")
+
--
2.14.1
More information about the Tarantool-patches
mailing list