@@ -848,6 +848,60 @@ static void php_sqlite3_callback_final(sqlite3_context *context) /* {{{ */
848848}
849849/* }}} */
850850
851+ static int php_sqlite3_callback_compare (void * coll , int a_len , const void * a , int b_len , const void * b ) /* {{{ */
852+ {
853+ php_sqlite3_collation * collation = (php_sqlite3_collation * )coll ;
854+ zval * * * zargs = NULL ;
855+ zval * retval = NULL ;
856+ int ret ;
857+
858+ TSRMLS_FETCH ();
859+
860+ collation -> fci .fci .size = (sizeof (collation -> fci .fci ));
861+ collation -> fci .fci .function_table = EG (function_table );
862+ collation -> fci .fci .function_name = collation -> cmp_func ;
863+ collation -> fci .fci .symbol_table = NULL ;
864+ collation -> fci .fci .object_ptr = NULL ;
865+ collation -> fci .fci .retval_ptr_ptr = & retval ;
866+ collation -> fci .fci .param_count = 2 ;
867+
868+ zargs = (zval * * * )safe_emalloc (2 , sizeof (zval * * ), 0 );
869+ zargs [0 ] = emalloc (sizeof (zval * ));
870+ zargs [1 ] = emalloc (sizeof (zval * ));
871+
872+ MAKE_STD_ZVAL (* zargs [0 ]);
873+ ZVAL_STRINGL (* zargs [0 ], a , a_len , 1 );
874+
875+ MAKE_STD_ZVAL (* zargs [1 ]);
876+ ZVAL_STRINGL (* zargs [1 ], b , b_len , 1 );
877+
878+ collation -> fci .fci .params = zargs ;
879+
880+ if ((ret = zend_call_function (& collation -> fci .fci , & collation -> fci .fcc TSRMLS_CC )) == FAILURE ) {
881+ php_error_docref (NULL TSRMLS_CC , E_WARNING , "An error occurred while invoking the compare callback" );
882+ }
883+
884+ zval_ptr_dtor (zargs [0 ]);
885+ zval_ptr_dtor (zargs [1 ]);
886+ efree (zargs [0 ]);
887+ efree (zargs [1 ]);
888+ efree (zargs );
889+
890+ //retval ought to contain a ZVAL_LONG by now
891+ // (the result of a comparison, i.e. most likely -1, 0, or 1)
892+ //I suppose we could accept any scalar return type, though.
893+ if (Z_TYPE_P (retval ) != IS_LONG ){
894+ php_error_docref (NULL TSRMLS_CC , E_WARNING , "An error occurred while invoking the compare callback (invalid return type). Collation behaviour is undefined." );
895+ }else {
896+ ret = Z_LVAL_P (retval );
897+ }
898+
899+ zval_ptr_dtor (& retval );
900+
901+ return ret ;
902+ }
903+ /* }}} */
904+
851905/* {{{ proto bool SQLite3::createFunction(string name, mixed callback [, int argcount])
852906 Allows registration of a PHP function as a SQLite UDF that can be called within SQL statements. */
853907PHP_METHOD (sqlite3 , createFunction )
@@ -958,6 +1012,53 @@ PHP_METHOD(sqlite3, createAggregate)
9581012}
9591013/* }}} */
9601014
1015+ /* {{{ proto bool SQLite3::createCollation(string name, mixed callback)
1016+ 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()). */
1017+ PHP_METHOD (sqlite3 , createCollation )
1018+ {
1019+ php_sqlite3_db_object * db_obj ;
1020+ zval * object = getThis ();
1021+ php_sqlite3_collation * collation ;
1022+ char * collation_name , * callback_name ;
1023+ int collation_name_len ;
1024+ zval * callback_func ;
1025+ db_obj = (php_sqlite3_db_object * )zend_object_store_get_object (object TSRMLS_CC );
1026+
1027+ SQLITE3_CHECK_INITIALIZED (db_obj , db_obj -> initialised , SQLite3 )
1028+
1029+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "sz" , & collation_name , & collation_name_len , & callback_func ) == FAILURE ) {
1030+ RETURN_FALSE ;
1031+ }
1032+
1033+ if (!collation_name_len ) {
1034+ RETURN_FALSE ;
1035+ }
1036+
1037+ if (!zend_is_callable (callback_func , 0 , & callback_name TSRMLS_CC )) {
1038+ php_sqlite3_error (db_obj , "Not a valid callback function %s" , callback_name );
1039+ efree (callback_name );
1040+ RETURN_FALSE ;
1041+ }
1042+ efree (callback_name );
1043+
1044+ collation = (php_sqlite3_collation * )ecalloc (1 , sizeof (* collation ));
1045+ if (sqlite3_create_collation (db_obj -> db , collation_name , SQLITE_UTF8 , collation , php_sqlite3_callback_compare ) == SQLITE_OK ) {
1046+ collation -> collation_name = estrdup (collation_name );
1047+
1048+ MAKE_STD_ZVAL (collation -> cmp_func );
1049+ MAKE_COPY_ZVAL (& callback_func , collation -> cmp_func );
1050+
1051+ collation -> next = db_obj -> collations ;
1052+ db_obj -> collations = collation ;
1053+
1054+ RETURN_TRUE ;
1055+ }
1056+ efree (collation );
1057+
1058+ RETURN_FALSE ;
1059+ }
1060+ /* }}} */
1061+
9611062typedef struct {
9621063 sqlite3_blob * blob ;
9631064 size_t position ;
@@ -1746,6 +1847,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3_createaggregate, 0, 0, 3)
17461847 ZEND_ARG_INFO (0 , argument_count )
17471848ZEND_END_ARG_INFO ()
17481849
1850+ ZEND_BEGIN_ARG_INFO_EX (arginfo_sqlite3_createcollation , 0 , 0 , 2 )
1851+ ZEND_ARG_INFO (0 , name )
1852+ ZEND_ARG_INFO (0 , callback )
1853+ ZEND_END_ARG_INFO ()
1854+
17491855ZEND_BEGIN_ARG_INFO_EX (argingo_sqlite3_openblob , 0 , 0 , 3 )
17501856 ZEND_ARG_INFO (0 , table )
17511857 ZEND_ARG_INFO (0 , column )
@@ -1809,6 +1915,7 @@ static zend_function_entry php_sqlite3_class_methods[] = {
18091915 PHP_ME (sqlite3 , querySingle , arginfo_sqlite3_querysingle , ZEND_ACC_PUBLIC )
18101916 PHP_ME (sqlite3 , createFunction , arginfo_sqlite3_createfunction , ZEND_ACC_PUBLIC )
18111917 PHP_ME (sqlite3 , createAggregate , arginfo_sqlite3_createaggregate , ZEND_ACC_PUBLIC )
1918+ PHP_ME (sqlite3 , createCollation , arginfo_sqlite3_createcollation , ZEND_ACC_PUBLIC )
18121919 PHP_ME (sqlite3 , openBlob , argingo_sqlite3_openblob , ZEND_ACC_PUBLIC )
18131920 PHP_ME (sqlite3 , enableExceptions , argingo_sqlite3_enableexceptions , ZEND_ACC_PUBLIC )
18141921 /* Aliases */
@@ -1905,6 +2012,7 @@ static void php_sqlite3_object_free_storage(void *object TSRMLS_DC) /* {{{ */
19052012{
19062013 php_sqlite3_db_object * intern = (php_sqlite3_db_object * )object ;
19072014 php_sqlite3_func * func ;
2015+ php_sqlite3_collation * collation ;
19082016
19092017 if (!intern ) {
19102018 return ;
@@ -1931,6 +2039,19 @@ static void php_sqlite3_object_free_storage(void *object TSRMLS_DC) /* {{{ */
19312039 efree (func );
19322040 }
19332041
2042+ while (intern -> collations ){
2043+ collation = intern -> collations ;
2044+ intern -> collations = collation -> next ;
2045+ if (intern -> initialised && intern -> db ){
2046+ sqlite3_create_collation (intern -> db , collation -> collation_name , SQLITE_UTF8 , NULL , NULL );
2047+ }
2048+ efree ((char * )collation -> collation_name );
2049+ if (collation -> cmp_func ){
2050+ zval_ptr_dtor (& collation -> cmp_func );
2051+ }
2052+ efree (collation );
2053+ }
2054+
19342055 if (intern -> initialised && intern -> db ) {
19352056 sqlite3_close (intern -> db );
19362057 intern -> initialised = 0 ;
0 commit comments