[tracker/unicode-lower] libtracker-data: Use libicu, libunistring or glib for fn:lower-case



commit b2b7c343f20be014c3554f3db6b36e7bacf1161f
Author: Philip Van Hoof <philip codeminded be>
Date:   Mon Mar 28 13:41:47 2011 +0200

    libtracker-data: Use libicu, libunistring or glib for fn:lower-case
    
    Fixes NB#240168.

 src/libtracker-data/tracker-db-interface-sqlite.c  |  142 ++++++++++++++++++++
 src/libtracker-data/tracker-sparql-expression.vala |    8 +-
 2 files changed, 149 insertions(+), 1 deletions(-)
---
diff --git a/src/libtracker-data/tracker-db-interface-sqlite.c b/src/libtracker-data/tracker-db-interface-sqlite.c
index 298a3c5..92456fa 100644
--- a/src/libtracker-data/tracker-db-interface-sqlite.c
+++ b/src/libtracker-data/tracker-db-interface-sqlite.c
@@ -34,6 +34,18 @@
 #include <libtracker-fts/tracker-fts.h>
 #endif
 
+#ifdef HAVE_LIBUNISTRING
+/* libunistring versions prior to 9.1.2 need this hack */
+#define _UNUSED_PARAMETER_
+#include <unistr.h>
+#include <unicase.h>
+#elif HAVE_LIBICU
+#include <unicode/utypes.h>
+#include <unicode/uregex.h>
+#include <unicode/ustring.h>
+#include <unicode/ucol.h>
+#endif
+
 #include "tracker-collation.h"
 
 #include "tracker-db-interface-sqlite.h"
@@ -521,6 +533,132 @@ function_sparql_regex (sqlite3_context *context,
 	sqlite3_result_int (context, ret);
 }
 
+#ifdef HAVE_LIBUNISTRING
+
+static void
+function_sparql_lower_case (sqlite3_context *context,
+                            int              argc,
+                            sqlite3_value   *argv[])
+{
+	const uint16_t *zInput;
+	uint16_t *zOutput;
+	size_t written = 0;
+	int nInput;
+	int nOutput;
+	const char *zLocale = NULL;
+
+	g_assert (argc == 1 || argc == 2);
+
+	if (argc == 2) {
+		zLocale = (const char *) sqlite3_value_text (argv[1]);
+	}
+
+	zInput = sqlite3_value_text16 (argv[0]);
+
+	if (!zInput) {
+		return;
+	}
+
+	nInput = sqlite3_value_bytes16 (argv[0]);
+
+	nOutput = nInput * 2 + 2;
+	zOutput = sqlite3_malloc (nOutput);
+
+	if (!zOutput) {
+		return;
+	}
+
+	u16_tolower (zInput, nInput, zLocale, NULL, &zOutput, &written);
+
+	sqlite3_result_text16 (context, zOutput, -1, sqlite3_free);
+}
+
+#elif HAVE_LIBICU
+
+static void
+function_sparql_lower_case (sqlite3_context *context,
+                            int              argc,
+                            sqlite3_value   *argv[])
+{
+	const UChar *zInput;
+	UChar *zOutput;
+	int nInput;
+	int nOutput;
+	const char *zLocale = 0;
+	UErrorCode status = U_ZERO_ERROR;
+
+	g_assert (argc == 1 || argc == 2);
+
+	if (argc == 2) {
+		zLocale = (const char *) sqlite3_value_text (argv[1]);
+	}
+
+	zInput = sqlite3_value_text16 (argv[0]);
+
+	if (!zInput) {
+		return;
+	}
+
+	nInput = sqlite3_value_bytes16 (argv[0]);
+
+	nOutput = nInput * 2 + 2;
+	zOutput = sqlite3_malloc (nOutput);
+
+	if (!zOutput) {
+		return;
+	}
+
+	u_strToLower (zOutput, nOutput/2, zInput, nInput/2, zLocale, &status);
+
+	if (!U_SUCCESS (status)){
+		char zBuf[128];
+		sqlite3_snprintf (128, zBuf, "ICU error: u_strToLower()/u_strToUpper(): %s", u_errorName (status));
+		zBuf[127] = '\0';
+		sqlite3_result_error(context, zBuf, -1);
+		return;
+	}
+
+	sqlite3_result_text16 (context, zOutput, -1, sqlite3_free);
+}
+
+#else /* GLib based */
+
+static void
+function_sparql_lower_case (sqlite3_context *context,
+                            int              argc,
+                            sqlite3_value   *argv[])
+{
+	const gchar *zInput;
+	gchar *zOutput;
+	int nInput;
+
+	g_assert (argc == 1 || argc == 2);
+
+	/* GLib API doesn't allow passing of locale (so we ignore argv[1]) */
+	/* GLib API works with UTF-8, so use the UTF-8 functions of SQLite too */
+
+	zInput = (const gchar*) sqlite3_value_text (argv[0]);
+
+	if (!zInput) {
+		return;
+	}
+
+	nInput = sqlite3_value_bytes (argv[0]);
+
+	if (!zOutput) {
+		return;
+	}
+
+	zOutput = g_utf8_strdown (zInput, nInput);
+
+	/* Unfortunately doesn't the GLib API allow us to pass pre-allocated memory,
+	 * so we can't use sqlite3_malloc and sqlite3_free here */
+
+	sqlite3_result_text (context, zOutput, -1, g_free);
+}
+
+#endif
+
 static inline int
 stmt_step (sqlite3_stmt *stmt)
 {
@@ -635,6 +773,10 @@ open_database (TrackerDBInterface  *db_interface,
 	                         db_interface, &function_sparql_uri_is_descendant,
 	                         NULL, NULL);
 
+	sqlite3_create_function (db_interface->db, "SparqlLowerCase", 1, SQLITE_ANY,
+	                         db_interface, &function_sparql_lower_case,
+	                         NULL, NULL);
+
 	sqlite3_extended_result_codes (db_interface->db, 0);
 	sqlite3_busy_timeout (db_interface->db, 100000);
 }
diff --git a/src/libtracker-data/tracker-sparql-expression.vala b/src/libtracker-data/tracker-sparql-expression.vala
index fdb0e2a..04fdcf3 100644
--- a/src/libtracker-data/tracker-sparql-expression.vala
+++ b/src/libtracker-data/tracker-sparql-expression.vala
@@ -411,12 +411,18 @@ class Tracker.Sparql.Expression : Object {
 			sql.append (" AS REAL)");
 
 			return PropertyType.DOUBLE;
-		} else if (uri == FN_NS + "lower-case") {
+		} else if (uri == TRACKER_NS + "ascii-lower-case") {
 			// conversion to string
 			sql.append ("lower (");
 			translate_expression_as_string (sql);
 			sql.append (")");
 			return PropertyType.STRING;
+		} else if (uri == FN_NS + "lower-case") {
+			// conversion to string
+			sql.append ("SparqlLowerCase (");
+			translate_expression_as_string (sql);
+			sql.append (")");
+			return PropertyType.STRING;
 		} else if (uri == FN_NS + "contains") {
 			// fn:contains('A','B') => 'A' GLOB '*B*'
 			sql.append ("(");



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]