php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Return to Bug #60871
Patch php_sqlite3_create_collation.patch revision 2012-01-29 02:52 UTC by macdewar at gmail dot com
revision 2012-01-24 19:10 UTC by macdewar at gmail dot com

Patch php_sqlite3_create_collation.patch for SQLite related Bug #60871

Patch version 2012-01-24 19:10 UTC

Return to Bug #60871 | Download this patch
This patch is obsolete

Obsoleted by patches:

Patch Revisions:

Developer: macdewar@gmail.com

diff -purN ext/sqlite3/CREDITS ext/sqlite3/CREDITS
--- ext/sqlite3/CREDITS	2010-12-11 10:57:34.000000000 -0400
+++ ext/sqlite3/CREDITS	2012-01-24 10:40:21.616705725 -0400
@@ -1,2 +1,2 @@
 SQLite3
-Scott MacVicar, Ilia Alshanetsky
+Scott MacVicar, Ilia Alshanetsky, Brad Dewar
diff -purN ext/sqlite3/php_sqlite3_structs.h ext/sqlite3/php_sqlite3_structs.h
--- ext/sqlite3/php_sqlite3_structs.h	2012-01-24 10:33:41.842377103 -0400
+++ ext/sqlite3/php_sqlite3_structs.h	2012-01-24 14:15:15.815330445 -0400
@@ -62,12 +62,23 @@ typedef struct _php_sqlite3_func {
 	struct php_sqlite3_fci afunc, astep, afini;
 } php_sqlite3_func;
 
+/* Structure for SQLite collation function */
+typedef struct _php_sqlite3_collation {
+	struct _php_sqlite3_collation *next;
+  
+	const char *coll_name;
+	zval *cmp_func;
+	struct php_sqlite3_fci fci;
+} php_sqlite3_collation;
+
 /* Structure for SQLite Database object. */
 typedef struct _php_sqlite3_db_object  {
 	zend_object zo;
 	int initialised;
 	sqlite3 *db;
 	php_sqlite3_func *funcs;
+	php_sqlite3_collation *collations;
+
 	zend_bool exception;
 
 	zend_llist free_list;
diff -purN ext/sqlite3/sqlite3.c ext/sqlite3/sqlite3.c
--- ext/sqlite3/sqlite3.c	2012-01-24 10:34:09.946023564 -0400
+++ ext/sqlite3/sqlite3.c	2012-01-24 14:34:06.951228044 -0400
@@ -851,6 +851,65 @@ static void php_sqlite3_callback_final(s
 }
 /* }}} */
 
+static int php_sqlite3_callback_compare(void *coll, int a_len, const void *a, int b_len, const void* b) /* {{{ */
+{
+	php_sqlite3_collation *collation = (php_sqlite3_collation*)coll;
+	zval ***zargs = NULL;
+	zval *retval = NULL;
+	int ret;
+
+	TSRMLS_FETCH();
+
+	//perhaps the rest should separated into its own function, but it's never called again, so it's inline here.
+
+	collation->fci.fci.size = (sizeof(collation->fci.fci));
+	collation->fci.fci.function_table = EG(function_table);
+	collation->fci.fci.function_name = collation->cmp_func;
+	collation->fci.fci.symbol_table = NULL;
+	collation->fci.fci.object_ptr = NULL;
+	collation->fci.fci.retval_ptr_ptr = &retval;
+	collation->fci.fci.param_count = 2;
+
+	zargs = (zval***)safe_emalloc(2, sizeof(zval**), 0);
+	zargs[0] = emalloc(sizeof(zval*));
+	zargs[1] = emalloc(sizeof(zval*));
+
+	MAKE_STD_ZVAL(*zargs[0]);
+	ZVAL_STRINGL(*zargs[0], a, a_len, 1);
+
+	MAKE_STD_ZVAL(*zargs[1]);
+	ZVAL_STRINGL(*zargs[1], b, b_len, 1);
+ 
+	collation->fci.fci.params = zargs;
+
+	if ((ret = zend_call_function(&collation->fci.fci, &coll->fci.fcc TSRMLS_CC)) == FAILURE) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the compare callback");
+	}
+
+	zval_ptr_dtor(zargs[0]);
+	zval_ptr_dtor(zargs[1]);
+	efree(zargs[0]);
+	efree(zargs[1]);
+	efree(zargs);
+
+	//retval ought to contain a ZVAL_LONG by now
+	// (the result of a comparison, i.e. most likely -1, 0, or 1)
+	//I suppose we could accept any scalar return type, though.
+	if (Z_TYPE_P(retval) != IS_LONG){
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the compare callback (invalid return type).  Collation behaviour is undefined.");
+	}else{
+		ret = Z_LVAL_P(retval);
+	}
+
+	zval_ptr_dtor(&retval);
+
+	return ret;
+}
+/* }}} */
+
+
+
+
 /* {{{ proto bool SQLite3::createFunction(string name, mixed callback [, int argcount])
    Allows registration of a PHP function as a SQLite UDF that can be called within SQL statements. */
 PHP_METHOD(sqlite3, createFunction)
@@ -961,6 +1020,54 @@ PHP_METHOD(sqlite3, createAggregate)
 }
 /* }}} */
 
+/* {{{ proto bool SQLite3::createCollation(string name, mixed callback)
+   Registers a PHP function as a comparator that can be used with the SQL COLLATE operator. Callback must accept two strings and return an integer (as strcmp()). */
+PHP_METHOD(sqlite3, createCollation)
+{
+	php_sqlite3_db_object *db_obj;
+	zval *object = getThis();
+	php_sqlite3_collation *collation;
+	char *collation_name, *callback_name;
+	int collation_name_len;
+	zval *callback_func;
+	db_obj = (php_sqlite3_db_object *)zend_object_store_get_object(object TSRMLS_CC);
+
+	SQLITE3_CHECK_INITIALIZED(db_obj, db_obj->initialised, SQLite3)
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &collation_name, &collation_name_len, &callback_func) == FAILURE) {
+		RETURN_FALSE;
+	}
+
+	if (!collation_name_len) {
+		RETURN_FALSE;
+	}
+
+	if (!zend_is_callable(callback_func, 0, &callback_name TSRMLS_CC)) {
+		php_sqlite3_error(db_obj, "Not a valid callback function %s", callback_name);
+		efree(callback_name);
+		RETURN_FALSE;
+	}
+	efree(callback_name);
+
+	collation = (php_sqlite3_collation *)ecalloc(1, sizeof(*collation));
+	if (sqlite3_create_collation(db_obj->db, collation_name, SQLITE_UTF8, ud_cmp_func, php_sqlite3_callback_compare) == SQLITE_OK) {
+		collation->collation_name = estrdup(collation_name);
+
+		MAKE_STD_ZVAL(collation->cmp_func);
+		MAKE_COPY_ZVAL(&callback_func, collation->cmp_func);
+
+		collation->next = db_obj->collations;
+		db_obj->collations = collation;
+
+		RETURN_TRUE;
+	}
+	efree(collation);
+
+	RETURN_FALSE;
+}
+/* }}} */
+
+
 typedef struct {
 	sqlite3_blob *blob;
 	size_t		 position;
@@ -1749,6 +1856,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3_c
 	ZEND_ARG_INFO(0, argument_count)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3_createcollation, 0, 0, 2)
+	ZEND_ARG_INFO(0, name)
+	ZEND_ARG_INFO(0, callback)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(argingo_sqlite3_openblob, 0, 0, 3)
 	ZEND_ARG_INFO(0, table)
 	ZEND_ARG_INFO(0, column)
@@ -1812,6 +1924,7 @@ static zend_function_entry php_sqlite3_c
 	PHP_ME(sqlite3,		querySingle,		arginfo_sqlite3_querysingle, ZEND_ACC_PUBLIC)
 	PHP_ME(sqlite3,		createFunction,		arginfo_sqlite3_createfunction, ZEND_ACC_PUBLIC)
 	PHP_ME(sqlite3,		createAggregate,	arginfo_sqlite3_createaggregate, ZEND_ACC_PUBLIC)
+	PHP_ME(sqlite3,		createCollation,	arginfo_sqlite3_createcollation, ZEND_ACC_PUBLIC)
 	PHP_ME(sqlite3,		openBlob,			argingo_sqlite3_openblob, ZEND_ACC_PUBLIC)
 	PHP_ME(sqlite3,		enableExceptions,	argingo_sqlite3_enableexceptions, ZEND_ACC_PUBLIC)
 	/* Aliases */
@@ -1908,6 +2021,7 @@ static void php_sqlite3_object_free_stor
 {
 	php_sqlite3_db_object *intern = (php_sqlite3_db_object *)object;
 	php_sqlite3_func *func;
+	php_sqlite3_collation *collation;
 
 	if (!intern) {
 		return;
@@ -1934,6 +2048,19 @@ static void php_sqlite3_object_free_stor
 		efree(func);
 	}
 
+	while (intern->collations){
+		collation = intern->collations;
+		intern->collations = collation->next;
+		if (intern->initialised && intern->db){
+			sqlite3_create_collation(intern->db, collation->collation_name, SQLITE_UTF8, NULL, NULL);
+		}
+		efree((char*)collation->collation_name);
+		if (cmp_func->func){
+			zval_ptr_dtor(&collation->cmp_func);
+		}
+		efree(collation);
+	}
+
 	if (intern->initialised && intern->db) {
 		sqlite3_close(intern->db);
 		intern->initialised = 0;
diff -purN ext/sqlite3/tests/sqlite3_36_create_collation.phpt ext/sqlite3/tests/sqlite3_36_create_collation.phpt
--- ext/sqlite3/tests/sqlite3_36_create_collation.phpt	1969-12-31 20:00:00.000000000 -0400
+++ ext/sqlite3/tests/sqlite3_36_create_collation.phpt	2012-01-24 11:26:35.183521376 -0400
@@ -0,0 +1,44 @@
+--TEST--
+Test SQLite3::createCollation() by adding strnatcmp() as an SQL COLLATE sequence
+--SKIPIF--
+<?php require_once dirname(__FILE__) .  '/skipif.inc'; ?>
+--FILE--
+<?php
+
+require_once dirname(__FILE__) .  '/new_db.inc';
+
+$db->createCollation('NAT', 'strnatcmp');
+
+$db->exec('CREATE TABLE t (s varchar(4))');
+
+$stmt = $db->prepare('INSERT INTO t VALUES (?)');
+foreach(array('a1', 'a10', 'a2') as $s){
+	$stmt->bindParam(1, $s);
+	$stmt->execute();
+}
+
+$defaultSort = $db->query('SELECT s FROM t ORDER BY s');             //memcmp() sort
+$naturalSort = $db->query('SELECT s FROM t ORDER BY s COLLATE NAT'); //strnatcmp() sort
+
+echo "default\n";
+while ($row = $defaultSort->fetchArray()){
+	echo $row['s'], "\n";
+}
+
+echo "natural\n";
+while ($row = $naturalSort->fetchArray()){
+	echo $row['s'], "\n";
+}
+
+$db->close();
+
+?>
+--EXPECT--
+default
+a1
+a10
+a2
+natural
+a1
+a2
+a10
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Mon Oct 18 21:03:39 2021 UTC