diff --git a/db/migrate/20180601160100_add_deb_version_cmp_function_to_db.rb b/db/migrate/20180601160100_add_deb_version_cmp_function_to_db.rb new file mode 100644 index 00000000000..2a839798288 --- /dev/null +++ b/db/migrate/20180601160100_add_deb_version_cmp_function_to_db.rb @@ -0,0 +1,125 @@ +class AddDebVersionCmpFunctionToDb < ActiveRecord::Migration[4.2] + # Implementation of version compare for debian packages + # Reference: http://man7.org/linux/man-pages/man5/deb-version.5.html + def self.up + unless connection.adapter_name.downcase.include?('sqlite') + execute " + CREATE OR REPLACE FUNCTION deb_version_cmp_num(_left text, _right text) RETURNS integer AS $$ + DECLARE + lint integer := 0; + rint integer := 0; + BEGIN + IF _left != '' THEN + lint := _left AS integer; + END IF; + IF _right != '' THEN + rint := _right AS integer; + END IF; + IF lint < rint THEN + RETURN -1; + ELSEIF lint > rint THEN + RETURN 1; + ELSE + RETURN 0; + END IF; + END; + $$ IMMUTABLE STRICT LANGUAGE plpgsql; + + CREATE OR REPLACE FUNCTION deb_version_cmp_al(_left text, _right text) + RETURNS integer + AS $$ + DECLARE + lpair text ARRAY[2]; + rpair text ARRAY[2]; + BEGIN + lpair := ARRAY['', _left]; + rpair := ARRAY['', _right]; + + LOOP + IF lpair[2] = '' AND rpair[2] = '' THEN + return 0; + END IF; + + lpair := regexp_matches(lpair[2], '(.?)(.*)'); + rpair := regexp_matches(rpair[2], '(.?)(.*)'); + + IF lpair[1] = rpair[1] THEN + CONTINUE; + END IF; + IF lpair[1] = '~' THEN + RETURN -1; + END IF; + IF rpair[1] = '~' THEN + RETURN 1; + END IF; + IF lpair[1] = '' THEN + RETURN -1; + END IF; + IF rpair[1] = '' THEN + RETURN 1; + END IF; + IF lpair[1] SIMILAR TO '[a-zA-Z]' THEN + IF rpair[1] SIMILAR TO '[a-zA-Z]' AND ascii(lpair[1]) > ascii(rpair[1]) THEN + RETURN 1; + END IF; + RETURN -1; + END IF; + IF rpair[1] SIMILAR TO '[a-zA-Z]' THEN + RETURN 1; + END IF; + IF ascii(lpair[1]) < ascii(rpair[1]) THEN + RETURN -1; + END IF; + RETURN 1; + END LOOP; + END; + $$ IMMUTABLE STRICT LANGUAGE plpgsql; + + CREATE OR REPLACE FUNCTION deb_version_cmp(_left text, _right text) + RETURNS integer + AS $$ + DECLARE + lpair text ARRAY[2]; + rpair text ARRAY[2]; + res integer; + BEGIN + lpair := ARRAY['', _left]; + rpair := ARRAY['', _right]; + + LOOP + IF lpair[2] = '' AND rpair[2] = '' THEN + return 0; + END IF; + + lpair := regexp_matches(lpair[2], '([^\\d]*)(.*)'); + rpair := regexp_matches(rpair[2], '([^\\d]*)(.*)'); + + res := deb_version_cmp_al(lpair[1], rpair[1]); + IF res != 0 THEN + RETURN res; + END IF; + + lpair := regexp_matches(lpair[2], '(\\d*)(.*)'); + rpair := regexp_matches(rpair[2], '(\\d*)(.*)'); + + res := deb_version_cmp_num(lpair[1], rpair[1]); + IF res != 0 THEN + RETURN res; + END IF; + END LOOP; + END + $$ IMMUTABLE STRICT LANGUAGE plpgsql; + " + end + end + + def self.down + unless connection.adapter_name.downcase.include?('sqlite') + execute " + DROP FUNCTION deb_version_cmp(text, text) CASCADE; + DROP FUNCTION deb_version_cmp_al(text, text) CASCADE; + DROP FUNCTION deb_version_cmp_num(text, text) CASCADE; + " + end + end +end