VOS.VOSCatFilterDETAPI

  • Topic
  • Discussion
  • VOS.VOSCatFilterDETAPI(Last) -- DAVWikiAdmin? , 2017-06-29 07:35:06 Edit WebDAV System Administrator 2017-06-29 07:35:06

    --  
    --  $Id: DET_CatFilter.sql,v 1.2 2007/03/28 10:48:50 source Exp $
    --
    --  This file is part of the OpenLink Software Virtuoso Open-Source (VOS)
    --  project.
    --  
    --  Copyright (C) 1998-2006 OpenLink Software
    --  
    --  This project is free software; you can redistribute it and/or modify it
    --  under the terms of the GNU General Public License as published by the
    --  Free Software Foundation; only version 2 of the License, dated June 1991.
    --  
    --  This program is distributed in the hope that it will be useful, but
    --  WITHOUT ANY WARRANTY; without even the implied warranty of
    --  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    --  General Public License for more details.
    --  
    --  You should have received a copy of the GNU General Public License along
    --  with this program; if not, write to the Free Software Foundation, Inc.,
    --  51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
    --  
    
    use DB
    ;
    
    -- CatFilter ID structure:
    -- for categories:
    -- vector (UNAME'CatFilter', detcol_id, null, schema_uri, vector (prop1uri, prop1catid, prop1decodedcatvalue, prop1crop, ..., propNuri, propNcatid, propNdecodedcatvalue, propNcrop))
    -- for resources:
    -- vector (UNAME'CatFilter', detcol_id, original_resource_id, schema_uri, vector (prop1uri, prop1catid, prop1decodedcatvalue, prop1crop, ..., propNuri, propNcatid, propNdecodedcatvalue, propNcrop))
    
    create function "CatFilter_DAV_AUTHENTICATE" (in id any, in what char(1), in req varchar, in auth_uname varchar, in auth_pwd varchar, in auth_uid integer)
    {
      declare cfc_id integer;
      declare rfc_spath, tmp_perms varchar;
      declare rfc_list_cond, rfc_del_action any;
      declare rc, spath_id, n integer;
      -- dbg_obj_princ ('CatFilter_DAV_AUTHENTICATE (', id, what, req, auth_uname, auth_pwd, auth_uid, ')');
      rfc_spath := null;
      if (DAV_HIDE_ERROR ("CatFilter_GET_CONDITION" (id[1], cfc_id, rfc_spath, rfc_list_cond, rfc_del_action)) is null)
        return -1;
      if (not ('110' like req))
        return -13; -- Internals of ResFilter are not executable.
      spath_id := DAV_SEARCH_ID (rfc_spath, 'C');
      if (not isinteger (spath_id))
        return -13;
      if (DAV_HIDE_ERROR (spath_id) is null)
        return spath_id;
      rc := DAV_AUTHENTICATE (spath_id, 'C', req, auth_uname, auth_pwd, auth_uid);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      if ('C' = what)
        {
          n := length (id[4]);
          if ((n=0) or (mod (n, 4) = 2))
            tmp_perms := '100';
          else if (length (rfc_del_action) < length (rfc_list_cond))
            {
              -- dbg_obj_princ ('del_action = ', rfc_del_action, 'rfc_list_cond = ', rfc_list_cond);
              tmp_perms := '100';
            }
          else
            tmp_perms := '110';
          if (not (tmp_perms like req))
            return -13;
          return auth_uid;
        }
      else if ('R' = what)
        {
          return DAV_AUTHENTICATE (id [2], 'R', req, auth_uname, auth_pwd, auth_uid);
        }
      return -14;
    }
    ;
    
    
    create function "CatFilter_GET_CONDITION" (in detcol_id integer, out cfc_id integer, out rfc_spath varchar, out rfc_list_cond any, out rfc_del_action any)
    {
      -- dbg_obj_princ ('CatFilter_GET_CONDITION (', detcol_id, '...)');
      whenever not found goto nf;
      if (isarray (detcol_id))
        return -20;
      select cast ("ResFilter_NORM" (PROP_VALUE) as integer) into cfc_id from WS.WS.SYS_DAV_PROP where PROP_NAME = 'virt:CatFilter-ID' and PROP_PARENT_ID = detcol_id and PROP_TYPE = 'C';
      select "ResFilter_NORM" (PROP_VALUE) into rfc_spath from WS.WS.SYS_DAV_PROP where PROP_NAME = 'virt:ResFilter-SearchPath' and PROP_PARENT_ID = detcol_id and PROP_TYPE = 'C';
      select "ResFilter_DECODE_FILTER" (PROP_VALUE) into rfc_list_cond from WS.WS.SYS_DAV_PROP where PROP_NAME = 'virt:ResFilter-ListCond' and PROP_PARENT_ID = detcol_id and PROP_TYPE = 'C';
      select "ResFilter_DECODE_FILTER" (PROP_VALUE) into rfc_del_action from WS.WS.SYS_DAV_PROP where PROP_NAME = 'virt:ResFilter-DelAction' and PROP_PARENT_ID = detcol_id and PROP_TYPE = 'C';
      return 0;
    nf:
      return -1;
    }
    ;
    
    
    create function "CatFilter_ENCODE_CATVALUE" (in val varchar) returns varchar
    {
      declare ses any;
      declare ctr, len integer;
      declare lastspace integer;
      if (val is null)
        return '! property is not set !';
      if (__tag (val) = 230)
        val := cast (val as varchar);
      ses := string_output ();
      len := length (val);
      if (len > 70)
        {
          val := subseq (val, 0, 65);
          lastspace := strrchr (val, ' ');
          if (lastspace is not null)
            val := subseq (val, 0, lastspace) || ' . . .';
          else
            val := val || '...';
        }
      if (len = 0)
        return '! empty property value !';
      len := length (val);
      for (ctr := 0; ctr < len; ctr := ctr + 1)
        {
          declare ch integer;
          ch := val [ctr];
          if ((ch < 32) or (ch = 47) or (ch = 92) or (ch = 37) or (ch = 58) or ((ch = 40) and (ctr = 0)))
            http (sprintf ('^%02x', ch), ses);
          else
            http (chr (ch), ses);
        }
      return string_output_string (ses);
    }
    ;
    
    
    create function "CatFilter_DECODE_CATVALUE" (in catval varchar, out crop integer)
    {
      declare val varchar;
      declare catvallen integer;
      if ('! empty property value !' = catval)
        {
          crop := 0;
          return '';
        }
      if ('! property is not set !' = catval)
        {
          crop := 4;
          return null;
        }
      catvallen := length (catval);
      if ((catvallen >= 6) and (subseq (catval, catvallen - 6) = ' . . .'))
        {
          crop := 1;
          catvallen := catvallen - 6;
          catval := subseq (catval, 0, catvallen);
        }
      else
      if ((catvallen >= 3) and (subseq (catval, catvallen - 3) = '...'))
        {
          crop := 2;
          catvallen := catvallen - 3;
          catval := subseq (catval, 0, catvallen);
        }
      else
        crop := 0;
      val := split_and_decode (catval, 0, '^');
      return val;
    }
    ;
    
    
    create function "CatFilter_PATH_PARTS_TO_FILTER" (inout path_parts any, out schema_uri varchar, out filter_data any) returns integer
    {
      declare prop_catnames varchar;
      declare pathctr, filtctr, pathlen integer;
      declare filt any;
      pathlen := length (path_parts) - 1;
      if (0 >= pathlen)
        {
          schema_uri := null;
          filter_data := null;
          return 0;
        }
    -- First of all, schema should be located.
    
    retry_after_recomp:
      whenever not found goto no_schema;
      select RS_URI, deserialize (blob_to_string(RS_PROP_CATNAMES)) into schema_uri, prop_catnames from WS.WS.SYS_RDF_SCHEMAS where RS_CATNAME = path_parts[0] and RS_PROP_CATNAMES is not null;
      filt := make_array (2 * (pathlen - 1), 'any');
      filtctr := 0;
      for (pathctr := 1; pathctr < pathlen; pathctr := pathctr + 2)
        {
          declare pos integer;
          pos := position (path_parts [pathctr], prop_catnames, 2, 6);
          if (0 = pos)
            {
              -- dbg_obj_princ ('CatFilter_PATH_PARTS_TO_FILTER (', path_parts, '...) failed to find catlabel ', path_parts [pathctr], ' in schema ', schema_uri);
              return -2;
            }
          filt [filtctr] := prop_catnames [pos - 2]; -- prop URI
          filt [filtctr + 1] := prop_catnames [pos]; -- prop catid
          if (pathctr < (pathlen - 1))
            {
              declare crop_mode integer;
              filt [filtctr + 2] := "CatFilter_DECODE_CATVALUE" (path_parts [pathctr + 1], crop_mode);
              filt [filtctr + 3] := crop_mode;
            }
          filtctr := filtctr + 4;
        }
      filter_data := filt;
      return 0;
    
    no_schema:
      -- dbg_obj_princ ('CatFilter_PATH_PARTS_TO_FILTER (', path_parts, '...) failed to find schema with catlabel ', path_parts [0]);
      if (exists (select top 1 1 from WS.WS.SYS_RDF_SCHEMAS where RS_CATNAME = path_parts[0] and RS_PROP_CATNAMES is null))
        {
          DAV_GET_RDF_SCHEMA_N3 ((select RS_URI from WS.WS.SYS_RDF_SCHEMAS where RS_CATNAME = path_parts[0]));
          goto retry_after_recomp;
        }
    
      return -1;
    }
    ;
    
    
    create procedure "CatFilter_ACC_FILTER_DATA" (inout filter any, inout filter_data any)
    {
      declare ctr, len integer;
      len := length (filter_data);
      len := len - mod (len, 4);
      for (ctr := 0; ctr < len; ctr := ctr + 4)
        {
          declare crop_mode integer;
          declare pred any;
          crop_mode := filter_data [ctr + 3];
          if (crop_mode = 0)
            pred := vector ('RDF_VALUE', '=', filter_data [ctr + 2], 'http://local.virt/DAV-RDF', filter_data [ctr]);
          else
          if (crop_mode = 4)
            pred := vector ('RDF_VALUE', 'is_null', 'http://local.virt/DAV-RDF', filter_data [ctr]);
          else
            pred := vector ('RDF_VALUE', 'starts_with', filter_data [ctr + 2], 'http://local.virt/DAV-RDF', filter_data [ctr]);
          vectorbld_acc (filter, pred);
        }
    }
    ;
    
    
    create function "CatFilter_DAV_SEARCH_ID_IMPL" (in detcol_id any, in path_parts any, in what char(1), inout cfc_id integer, inout rfc_spath varchar, inout rfc_list_cond any, inout rfc_del_action any, inout filter_data any) returns any
    {
      declare schema_catname, schema_uri, res_name, colpath, orig_fnameext varchar;
      declare prop_catnames, filter, orig_id any;
      declare path_len, len, ctr integer;
      declare execstate, execmessage varchar;
      declare execmeta, execrows any;
      declare qry_text varchar;
      -- dbg_obj_princ ('CatFilter_DAV_SEARCH_ID_IMPL (', detcol_id, path_parts, what, ')');
      path_len := length (path_parts);
      if (not (isstring (rfc_spath)))
        {
          if (0 > "CatFilter_GET_CONDITION" (detcol_id, cfc_id, rfc_spath, rfc_list_cond, rfc_del_action))
    	{
    	  -- dbg_obj_princ ('broken filter - no items');
    	  return -1;
    	}
        }
      if (0 = path_len)
        return -1;
      res_name := path_parts [path_len - 1];
      if ('' = res_name)
        {
          if ('R' = what)
            {
              -- dbg_obj_princ ('resource with trailing slash - no items');
              return -1;
            }
        }
      else
        {
          if ('C' = what)
            {
              -- dbg_obj_princ ('collection without a trailing slash - no items');
              return -1;
            }
          if (1 = path_len)
            {
              -- dbg_obj_princ ('resource with path length = 1 - no items at depth of schemas');
              return -1;
            }
          if (2 = path_len)
            {
              -- dbg_obj_princ ('resource with path length = 2 - no items at depth of first property name under schemas');
              return -1;
            }
          if (1 = mod (path_len, 2))
            {
              -- dbg_obj_princ ('resource with even path length - no items at depth of distinct values');
              return -1;
            }
        }
      if (0 > "CatFilter_PATH_PARTS_TO_FILTER" (path_parts, schema_uri, filter_data))
        {
          -- dbg_obj_princ ('failed to convert path parts to filter - no items');
          return -1;
        }
      if ('C' = what)
        return vector (UNAME'CatFilter', detcol_id, null, schema_uri, case (length (filter_data)) when 0 then null else filter_data end);
      "ResFilter_FNSPLIT" (res_name, colpath, orig_fnameext, orig_id);
    
      -- dbg_obj_princ ('CatFilter_DAV_SEARCH_ID_IMPL: ', path_parts, colpath, orig_fnameext, orig_id, rfc_spath, rfc_list_cond, filter_data);
    
      if (isarray (orig_id)) -- TODO: remove this and make better processing to return -1 if path contains criteria that filter out orig_id
        return orig_id;
      len := length (filter_data);
      vectorbld_init (filter);
      "CatFilter_ACC_FILTER_DATA" (filter, filter_data);
      vectorbld_concat_acc (filter, get_keyword ('', rfc_list_cond));
      if (orig_id is not null)
        {
          if (isinteger (orig_id))
            vectorbld_acc (filter, vector ('RES_ID', '=', orig_id));
          else -- never happens for a while
            vectorbld_acc (filter, vector ('RES_ID_SERIALIZED', '=', serialize (orig_id)));
        }
      vectorbld_final (filter);
    
      qry_text := '
        select top 2 RES_ID
        from WS.WS.SYS_DAV_RES as _top ' || DAV_FC_PRINT_WHERE (filter, coalesce ((select COL_OWNER from WS.WS.SYS_DAV_COL where COL_ID = detcol_id), -1)) || ' and (_top.RES_NAME = ?) and (_top.RES_FULL_PATH between ? and ?)';
      -- dbg_obj_princ ('about to exec:\n', qry_text, '\nrfc_spath = ', rfc_spath);
      exec (qry_text,
        execstate, execmessage, vector (orig_fnameext, rfc_spath, DAV_COL_PATH_BOUNDARY (rfc_spath)), 100000000, execmeta, execrows );
      len := length (execrows);
      if (len <> 1)
        return -1;
      return vector (UNAME'CatFilter', detcol_id, execrows[0][0], schema_uri, case (length (filter_data)) when 0 then null else filter_data end);
    }
    ;
    
    
    create function "CatFilter_DAV_AUTHENTICATE_HTTP" (in id any, in what char(1), in req varchar, in can_write_http integer, inout a_lines any, inout a_uname varchar, inout a_pwd varchar, inout a_uid integer, inout a_gid integer, inout _perms varchar) returns integer
    {
      declare cfc_id integer;
      declare rfc_spath, tmp_perms varchar;
      declare rfc_list_cond, rfc_del_action any;
      declare rc, spath_id, n integer;
      -- dbg_obj_princ ('"CatFilter_DAV_AUTHENTICATE_HTTP" (', id, what, req, can_write_http, a_lines, a_uname, a_pwd, a_uid, a_gid, _perms, ')');
      rfc_spath := null;
      rc := DAV_HIDE_ERROR ("CatFilter_GET_CONDITION" (id[1], cfc_id, rfc_spath, rfc_list_cond, rfc_del_action));
      if (DAV_HIDE_ERROR (rc) is null)
        {
          -- dbg_obj_princ ('"CatFilter_DAV_AUTHENTICATE_HTTP" failed at CatFilter_GET_CONDITION, ', rc);
          return rc;
        }
      if (not ('110' like req))
        return -13; -- Internals of ResFilter/CatFilter are not executable.
      spath_id := DAV_SEARCH_ID (rfc_spath, 'C');
      -- dbg_obj_princ ('rfc_spath = ', rfc_spath, ', spath_id = ', spath_id);
      if (not isinteger (spath_id))
        return -13;
      if (DAV_HIDE_ERROR (spath_id) is null)
        return spath_id;
      rc := DAV_AUTHENTICATE_HTTP (spath_id, 'C', req, can_write_http, a_lines, a_uname, a_pwd, a_uid, a_gid, _perms);
      if (DAV_HIDE_ERROR (rc) is null)
        {
          return rc;
        }
      if ('C' = what)
        {
          n := length (id[4]);
          if ((n=0) or (mod (n, 4) = 2))
            tmp_perms := '100';
          else if (length (rfc_del_action) < length (rfc_list_cond))
            {
              -- dbg_obj_princ ('del_action = ', rfc_del_action, 'rfc_list_cond = ', rfc_list_cond);
              tmp_perms := '100';
            }
          else
            tmp_perms := '110';
          if (not (tmp_perms like req))
            return -13;
          return a_uid;
        }
      else if ('R' = what)
        {
          return DAV_AUTHENTICATE_HTTP (id[2], 'R', req, can_write_http, a_lines, a_uname, a_pwd, a_uid, a_gid, _perms);
        }
      return -14;
    }
    ;
    
    
    create function "CatFilter_DAV_GET_PARENT" (in id any, in st char(1), in path varchar) returns any
    {
      -- dbg_obj_princ ('CatFilter_DAV_GET_PARENT (', id, st, path, ')');
      if (st = 'R')
        {
          id [2] := null;
          return id;
        }
      else if (st = 'C')
        {
          declare vlen integer;
          vlen := length (id[4]);
          if (vlen = 0)
            return id [1];
          id [4] := subseq (id [4], 0, vlen - 1);
          return id;
        }
      return -20;
    }
    ;
    
    
    create function "CatFilter_DAV_COL_CREATE" (in detcol_id any, in path_parts any, in permissions varchar, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('CatFilter_DAV_COL_CREATE (', detcol_id, path_parts, permissions, uid, gid, auth_uid, ')');
      return -20;
    }
    ;
    
    
    create function "CatFilter_DAV_COL_MOUNT" (in detcol_id any, in path_parts any, in full_mount_path varchar, in mount_det varchar, in permissions varchar, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('CatFilter_DAV_COL_MOUNT (', detcol_id, path_parts, full_mount_path, mount_det, permissions, uid, gid, auth_uid, ')');
      return -20;
    }
    ;
    
    
    create function "CatFilter_DAV_COL_MOUNT_HERE" (in parent_id any, in full_mount_path varchar, in permissions varchar, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('CatFilter_DAV_COL_MOUNT (', parent_id, full_mount_path, permissions, uid, gid, auth_uid, ')');
      return -20;
    }
    ;
    
    
    create function "CatFilter_DAV_DELETE" (in detcol_id any, in path_parts any, in what char(1), in silent integer, in auth_uid integer) returns integer
    {
      declare rc integer;
      declare cfc_id integer;
      declare rfc_spath, propname varchar;
      declare rfc_list_cond, rfc_del_action any;
      declare orig_id, filter_data, whole_rdf, vals, new_rdf any;
      -- dbg_obj_princ ('CatFilter_DAV_DELETE (', detcol_id, path_parts, what, silent, auth_uid, ')');
      rfc_spath := null;
      orig_id := "CatFilter_DAV_SEARCH_ID_IMPL" (detcol_id, path_parts, what, cfc_id, rfc_spath, rfc_list_cond, rfc_del_action, filter_data);
      if (DAV_HIDE_ERROR (orig_id) is null)
        return orig_id;
      if (length (rfc_del_action) < length (rfc_list_cond))
        {
          -- dbg_obj_princ ('del_action = ', rfc_del_action, 'rfc_list_cond = ', rfc_list_cond);
          return -13;
        }
      if ('R' <> what)
        return -20;
      whole_rdf := coalesce ((select PROP_VALUE from WS.WS.SYS_DAV_PROP where PROP_NAME = 'http://local.virt/DAV-RDF' and PROP_TYPE = 'R' and PROP_PARENT_ID = orig_id [2]));
      if (whole_rdf is null)
        return -1;
      if (not isstring (whole_rdf))
        whole_rdf := blob_to_string (whole_rdf);
      whole_rdf := xml_tree_doc (deserialize (whole_rdf));
      propname := filter_data [length (filter_data) - 4];
      -- dbg_obj_princ ('rdf is ', whole_rdf);
      vals := xpath_eval (
          '[xmlns:virt="virt"] /virt:rdf/virt:top-res/virt:prop[*[1][name(.) = \044propname]][virt:value]',
          whole_rdf, 0, vector ('propname', filter_data [length (filter_data) - 4]) );
      -- dbg_obj_princ ('vals=', vals);
      foreach (any val in vals) do
        {
          declare cval, decenc_val varchar;
          declare crop integer;
          cval := cast (xpath_eval ('[xmlns:virt="virt"] string (virt:value)', val, 1) as varchar);
          decenc_val := "CatFilter_DECODE_CATVALUE" ("CatFilter_ENCODE_CATVALUE" (cval), crop);
          -- dbg_obj_princ ('Found value ', val, ' of ', propname, 'decenc=', decenc_val);
          if (decenc_val = filter_data [length (filter_data) - 2])
            {
              -- dbg_obj_princ ('matches');
              XMLReplace (whole_rdf, val, null);
            }
        }
      new_rdf := xte_node (xte_head (UNAME' root'), whole_rdf);
      update WS.WS.SYS_DAV_PROP set prop_value = serialize (new_rdf) where PROP_NAME = 'http://local.virt/DAV-RDF' and PROP_TYPE = 'R' and PROP_PARENT_ID = orig_id [2];
      return 0;
    }
    ;
    
    
    create function "CatFilter_FILTER_TO_CONDITION" (inout schema_uri varchar, inout filter_data any, inout cond any) returns integer
    {
      declare ctr, len integer;
      if (schema_uri is null)
        return -13;
      len := length (filter_data);
      if ((len = 0) or (0 <> mod (len, 4)))
        return -13;
      vectorbld_init (cond);
      for (ctr := 0; ctr < len; ctr := ctr + 4)
        {
          declare sample varchar;
          declare crop integer;
          crop := filter_data [ctr + 3];
    --TBD proper support of crop 1,2,4
          if (2 = crop)
            return -13;
          sample := filter_data [ctr + 2];
          if (1 = crop)
            return -13; --TBD: search for appropriate full text and set sample to the full text.
          vectorbld_acc (cond, vector ('RDF_VALUE', '=', sample, 'http://local.virt/DAV-RDF', filter_data [ctr]));
        }
      vectorbld_final (cond);
      return 0;
    }
    ;
    
    create function "CatFilter_DAV_RES_UPLOAD" (in detcol_id any, in path_parts any, inout content any, in type varchar, in permissions varchar, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      declare rc integer;
      declare cfc_id integer;
      declare rfc_spath, propname, schema_uri, _colpath, fnameext, orig_fnameext, orig_fullpath varchar;
      declare rfc_list_cond, rfc_del_action any;
      declare orig_id, filter_data, fit_cond any;
      -- dbg_obj_princ ('CatFilter_DAV_RES_UPLOAD (', detcol_id, path_parts, ', [content], ', type, permissions, uid, gid, auth_uid, ')');
      rfc_spath := null;
      rc := "CatFilter_GET_CONDITION" (detcol_id, cfc_id, rfc_spath, rfc_list_cond, rfc_del_action);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      if (length (rfc_del_action) < length (rfc_list_cond))
        {
          -- dbg_obj_princ ('del_action = ', rfc_del_action, 'rfc_list_cond = ', rfc_list_cond);
          return -13;
        }
      schema_uri := null;
      rc := "CatFilter_PATH_PARTS_TO_FILTER" (path_parts, schema_uri, filter_data);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      rc := "CatFilter_FILTER_TO_CONDITION" (schema_uri, filter_data, fit_cond);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      fit_cond := vector ('', vector_concat (fit_cond, get_keyword ('', rfc_list_cond)));
      -- dbg_obj_princ ('fit_cond=', fit_cond);
      fnameext := path_parts [length (path_parts) - 1];
      "ResFilter_FNSPLIT" (fnameext, _colpath, orig_fnameext, orig_id);
      orig_fullpath := null;
      if (orig_id is not null)
        orig_fullpath := DAV_HIDE_ERROR (DAV_SEARCH_PATH (orig_id, 'R'));
      if (orig_fullpath is null)
        orig_fullpath := DAV_CONCAT_PATH (rfc_spath, orig_fnameext);
      orig_id := DAV_RES_UPLOAD_STRSES_INT (
        orig_fullpath,
        content, '',
        permissions, '', '',
        null, null, 0,
        null, null, null,
        uid, gid, 1 );
      -- dbg_obj_princ ('Will call "ResFilter_FIT_INTO_CONDITION" (', orig_id, 'R', fit_cond, auth_uid);
      if (DAV_HIDE_ERROR (orig_id) is null)
        return orig_id;
      if (not (isinteger (orig_id)))
        return -13;
      "ResFilter_FIT_INTO_CONDITION" (orig_id, 'R', fit_cond, auth_uid);
      return vector (UNAME'CatFilter', detcol_id, orig_id, schema_uri, filter_data);
    }
    ;
    
    create function "CatFilter_DAV_PROP_REMOVE" (in id any, in st char(0), in propname varchar, in silent integer, in auth_uid integer) returns integer
    {
      -- dbg_obj_princ ('CatFilter_DAV_PROP_REMOVE (', id, st, propname, silent, auth_uid, ')');
      if (st <> 'R')
        return -1;
      id := id[2];
      if (isarray (id))
        return call (cast (id[0] as varchar) || '_DAV_PROP_REMOVE') (id, st, propname, silent, auth_uid);
      return DAV_PROP_REMOVE_RAW (id, st, propname, silent, auth_uid);
    }
    ;
    
    
    create function "CatFilter_DAV_PROP_SET" (in id any, in st char(0), in propname varchar, in propvalue any, in overwrite integer, in auth_uid integer) returns any
    {
      declare pid integer;
      declare resv any;
      -- dbg_obj_princ ('CatFilter_DAV_PROP_SET (', id, st, propname, propvalue, overwrite, auth_uid, ')');
      if (st <> 'R')
        return -1;
      id := id[2];
      if (isarray (id))
        return call (cast (id[0] as varchar) || '_DAV_PROP_SET') (id, st, propname, propvalue, overwrite, auth_uid);
      return DAV_PROP_SET_RAW (id, st, propname, propvalue, overwrite, auth_uid);
    }
    ;
    
    
    create function "CatFilter_DAV_PROP_GET" (in id any, in what char(0), in propname varchar, in auth_uid integer)
    {
      declare ret varchar;
      -- dbg_obj_princ ('CatFilter_DAV_PROP_GET (', id, what, propname, auth_uid, ')');
      id := id[2];
      if (isarray (id))
        return call (cast (id[0] as varchar) || '_DAV_PROP_GET') (id, what, propname, auth_uid);
      if (propname[0] = 58)
        return DAV_PROP_GET_INT (id, what, propname, 0, null, null, auth_uid);
      whenever not found goto no_prop;
      select blob_to_string (PROP_VALUE) into ret from WS.WS.SYS_DAV_PROP where PROP_NAME = propname and PROP_PARENT_ID = id and PROP_TYPE = what;
      return ret;
    
    no_prop:
        return -11;
    }
    ;
    
    
    create function "CatFilter_DAV_PROP_LIST" (in id any, in what char(0), in propmask varchar, in auth_uid integer)
    {
      declare ret any;
      -- dbg_obj_princ ('CatFilter_DAV_PROP_LIST (', id, what, propmask, auth_uid, ')');
      id := id[2];
      if (isarray (id))
        return call (cast (id[0] as varchar) || '_DAV_PROP_LIST') (id, what, propmask, auth_uid);
      vectorbld_init (ret);
      for select PROP_NAME, PROP_VALUE from WS.WS.SYS_DAV_PROP where PROP_NAME like propmask and PROP_PARENT_ID = id and PROP_TYPE = what do {
          vectorbld_acc (ret, vector (PROP_NAME, blob_to_string (PROP_VALUE)));
        }
      vectorbld_final (ret);
      return ret;
    }
    ;
    
    
    create function "CatFilter_DAV_DIR_SINGLE" (in id any, in what char(0), in path any, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('CatFilter_DAV_DIR_SINGLE (', id, what, path, auth_uid, ')');
      if ('C' = what)
        {
          declare cfc_id integer;
          declare rfc_spath varchar;
          declare rfc_list_cond, rfc_del_action any;
          declare loc_name, subcol_perms varchar;
          declare set_readonly integer;
          if (0 > "CatFilter_GET_CONDITION" (id[1], cfc_id, rfc_spath, rfc_list_cond, rfc_del_action))
    	{
    	-- dbg_obj_princ ('broken filter - no items');
    	return -1;
    	}
          subcol_perms := coalesce ((select COL_PERMS from WS.WS.SYS_DAV_COL where COL_ID = id[1]), '000000000N');
          subcol_perms[2] := 48; subcol_perms[5] := 48; subcol_perms[8] := 48; -- Can't execute in CatFilter so place zero chars.
          set_readonly := 0;
          if (length (rfc_del_action) < length (rfc_list_cond))
    	{
              -- dbg_obj_princ ('del_action = ', rfc_del_action, 'rfc_list_cond = ', rfc_list_cond);
              set_readonly := 1;
    	}
          else
            {
              declare filt_len integer;
              filt_len := length (id[4]);
              if ((0 = filt_len) or (mod (filt_len, 4) = 2))
                set_readonly := 1;
            }
          if (set_readonly)
            {
              subcol_perms[1] := 48; subcol_perms[4] := 48; subcol_perms[7] := 48; -- Can't write in CatFilter so place zero chars.
            }
          loc_name := path [length (path) - 2];
          return vector (path, 'C', 0, now (), id, subcol_perms, 0, auth_uid, now (), 'dav/unix-directory', loc_name );
        }
      if (isarray (id[2]))
        {
          declare diritem any;
          declare merged varchar;
          diritem := call (cast (id[0] as varchar) || '_DAV_DIR_SINGLE') (id[2], what, path, auth_uid);
          merged := "ResFilter_FNMERGE" (diritem[10], id[2]);
          diritem[0] := DAV_CONCAT_PATH (path, merged);
          diritem[10] := merged;
          -- dbg_obj_princ ('About to return in DAV_DIR_SINGLE: ', diritem);
          return diritem;
        }
      for select RES_FULL_PATH, RES_ID, length (RES_CONTENT) as clen, RES_MOD_TIME,
        RES_PERMS, RES_GROUP, RES_OWNER, RES_CR_TIME, RES_TYPE, RES_NAME as r1_RES_NAME
      from WS.WS.SYS_DAV_RES r1
      where RES_ID = id[2]
      do
        {
          declare merged varchar;
          -- dbg_obj_princ ('About to return in DAV_DIR_SINGLE: ', r1_RES_NAME, RES_ID);
          if (regexp_parse ('^([^/][^./]*) -Rf((Id[1-9][0-9]*)|([A-Z][A-Za-z0-9]+)-([A-Za-z0-9~+-]*))([.][^/]*)?\044', r1_RES_NAME, 0)) -- Suspicious names should be qualified
            {
              merged := "ResFilter_FNMERGE" (r1_RES_NAME, RES_ID);
            }
          else
            {
              declare cfc_id integer;
    	  declare rfc_spath varchar;
    	  declare rfc_list_cond, rfc_del_action varchar;
    	  declare tmp_comp, namesakes any;
              declare namesakes_no integer;
    	  if (0 > "CatFilter_GET_CONDITION" (id[1], cfc_id, rfc_spath, rfc_list_cond, rfc_del_action))
    	    {
    	      -- dbg_obj_princ ('broken filter - bad id in DIR_SINGLE');
    	      return -1;
    	    }
              tmp_comp := vector ('',
                vector_concat (
                  vector (vector ('RES_NAME', '=', r1_RES_NAME)),
                  get_keyword ('', rfc_list_cond) ) );
    	  namesakes := DAV_DIR_FILTER_INT (rfc_spath, 1, tmp_comp, null, null, auth_uid);
    	  namesakes_no := length (namesakes);
    	  if (0 = namesakes_no)
    	    return -1;
    	  if (1 < namesakes_no)
    	    merged := "ResFilter_FNMERGE" (r1_RES_NAME, RES_ID);
    	  else
    	    merged := r1_RES_NAME;
            }
          path [length (path) - 1] := merged;
    --                   0                            1    2     3
          return vector (DAV_CONCAT_PATH ('/', path), 'R', clen, RES_MOD_TIME,
    --       4   5          6          7          8            9         10
    	 id, RES_PERMS, RES_GROUP, RES_OWNER, RES_CR_TIME, RES_TYPE, merged);
        }
      return -1;
    }
    ;
    
    
    create function "CatFilter_LIST_SCHEMAS" (in rfc_spath varchar, inout rfc_list_cond any, in auth_uid integer) returns any
    {
      return (select VECTOR_AGG (vector (RS_URI, RS_CATNAME)) from WS.WS.SYS_RDF_SCHEMAS);
    }
    ;
    
    create function "CatFilter_LIST_SCHEMA_PROPS" (in rfc_spath varchar, inout rfc_list_cond any, inout schema_uri varchar, inout filter_data any, in auth_uid integer) returns any
    {
      declare prop_catnames, res any;
      declare len, ctr integer;
      -- dbg_obj_princ ('CatFilter_LIST_SCHEMA_PROPS (', rfc_spath, rfc_list_cond, schema_uri, filter_data, auth_uid, ')');
      vectorbld_init (res);
    
    retry_after_recomp:
      whenever not found goto schema_nf;
      select deserialize (cast (RS_PROP_CATNAMES as varchar)) into prop_catnames from WS.WS.SYS_RDF_SCHEMAS where RS_URI = schema_uri and RS_PROP_CATNAMES is not null;
      len := length (prop_catnames);
      for (ctr := 0; ctr < len; ctr := ctr + 6)
        {
          if (0 = position (prop_catnames [ctr], filter_data, 1, 4))
            vectorbld_acc (res, vector (prop_catnames [ctr], prop_catnames [ctr + 1]));
        }
      vectorbld_final (res);
      return res;
    
    schema_nf:
      if (exists (select top 1 1 from WS.WS.SYS_RDF_SCHEMAS where RS_URI = schema_uri and RS_PROP_CATNAMES is null))
        {
          DAV_GET_RDF_SCHEMA_N3 (schema_uri);
          goto retry_after_recomp;
        }
      return vector();
    }
    ;
    
    
    create procedure "CatFilter_GET_RDF_INVERSE_HITS_DISTVALS" (in cfc_id integer, inout filter_data any, inout distval_dict any, in auth_uid integer)
    {
      declare filter_length, p0_id, p1_id, p2_id, p3_id, p4_id, res0_id, res1_id, res2_id, res3_id, res4_id, res_last_id, res_id_max integer;
      declare plast_id integer;
      declare p0_val, p1_val, p2_val, p3_val, p4_val, v_last, v_max varchar;
      declare auth_gid integer;
      declare acl_bits, hit_ids any;
      declare c_last1 cursor for select             DRI_CATVALUE from WS.WS.SYS_DAV_RDF_INVERSE
        where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = plast_id and (v_max is null or DRI_CATVALUE > v_max) and
          exists (select top 1 1 from WS.WS.SYS_DAV_RES where RES_ID = DRI_RES_ID and case (DAV_CHECK_PERM (RES_PERMS, '1__', auth_uid, auth_gid, RES_GROUP, RES_OWNER)) when 0 then WS.WS.ACL_IS_GRANTED (RES_ACL, auth_uid, acl_bits) else 1 end);
      declare c_last2 cursor for select DRI_RES_ID, DRI_CATVALUE from WS.WS.SYS_DAV_RDF_INVERSE
        where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = plast_id and (v_max is null or DRI_CATVALUE > v_max) and
          exists (select top 1 1 from WS.WS.SYS_DAV_RES where RES_ID = DRI_RES_ID and case (DAV_CHECK_PERM (RES_PERMS, '1__', auth_uid, auth_gid, RES_GROUP, RES_OWNER)) when 0 then WS.WS.ACL_IS_GRANTED (RES_ACL, auth_uid, acl_bits) else 1 end);
      declare c0 cursor for select DRI_RES_ID from WS.WS.SYS_DAV_RDF_INVERSE where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = p0_id and DRI_CATVALUE = p0_val and DRI_RES_ID >= res_id_max;
      declare c1 cursor for select DRI_RES_ID from WS.WS.SYS_DAV_RDF_INVERSE where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = p1_id and DRI_CATVALUE = p1_val and DRI_RES_ID >= res_id_max;
      declare c2 cursor for select DRI_RES_ID from WS.WS.SYS_DAV_RDF_INVERSE where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = p2_id and DRI_CATVALUE = p2_val and DRI_RES_ID >= res_id_max;
      declare c3 cursor for select DRI_RES_ID from WS.WS.SYS_DAV_RDF_INVERSE where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = p3_id and DRI_CATVALUE = p3_val and DRI_RES_ID >= res_id_max;
      declare c4 cursor for select DRI_RES_ID from WS.WS.SYS_DAV_RDF_INVERSE where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = p4_id and DRI_CATVALUE = p4_val and DRI_RES_ID >= res_id_max;
      -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS (', cfc_id, filter_data, auth_uid, ')');
      filter_length := length (filter_data);
      plast_id := filter_data [filter_length - 1];
      res_id_max := 0;
      v_max := null;
      auth_gid := coalesce ((select U_GROUP from WS.WS.SYS_DAV_USER where U_ID = auth_uid), 0);
      acl_bits := DAV_REQ_CHARS_TO_BITMASK ('1__');
    
      if (filter_length = 2) -- distinct propvals with no filtering in front -- a special case
        {
          whenever not found goto nf_c_last1;
          -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: distinct propvals of ', plast_id, ' in ', cfc_id);
          open c_last1 (prefetch 1);
          while (1)
            {
              fetch c_last1 into v_last;
              -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: v_last is ', v_last, ' v_max is ', v_max);
              if (v_max is null or (v_last > v_max))
                {
                  v_max := v_last; -- note that vectorbld_acc() will destroy the value of v_last so this assignment should be before vectorbld_acc().
                  dict_put (distval_dict, v_last, 1);
                }
            }
    nf_c_last1:
          close c_last1;
          return;
        }
    
      res0_id := 0;
      res1_id := 0;
      res2_id := 0;
      res3_id := 0;
      res4_id := 0;
      hit_ids := dict_new ();
    
      p0_id := filter_data [1];
      p0_val := "CatFilter_ENCODE_CATVALUE" (filter_data [2]);
      if (filter_length = 6) -- distinct propvals with 1 fixed property
        {
          whenever not found goto get_distincts_0;
          open c0 (prefetch 1);
          while (1)
            {
              while (res0_id <= res_id_max)
                fetch c0 into res0_id;
              res_id_max := res0_id;
              -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: put hit ', res_id_max);
              dict_put (hit_ids, res_id_max, 1);
            }
        }
    
      p1_id := filter_data [4+1];
      p1_val := "CatFilter_ENCODE_CATVALUE" (filter_data [4+2]);
      if (filter_length = 10) -- distinct propvals with 2 fixed property
        {
          whenever not found goto get_distincts_1;
          open c0 (prefetch 1);
          open c1 (prefetch 1);
          while (1)
            {
              while (res0_id <= res_id_max) fetch c0 into res0_id;
              if (res0_id > res_id_max) res_id_max := res0_id;
              while (res1_id < res_id_max) fetch c1 into res1_id;
              if (res1_id > res_id_max) res_id_max := res1_id;
              if ((res0_id = res_id_max) and (res1_id = res_id_max))
                {
                  -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: put hit ', res_id_max);
                  dict_put (hit_ids, res_id_max, 1);
                }
              else
                res_id_max := res_id_max + 1;
    
            }
        }
    
      p2_id := filter_data [8+1];
      p2_val := "CatFilter_ENCODE_CATVALUE" (filter_data [8+2]);
      if (filter_length = 14) -- distinct propvals with 3 fixed property
        {
          whenever not found goto get_distincts_2;
          open c0 (prefetch 1);
          open c1 (prefetch 1);
          open c2 (prefetch 1);
          while (1)
            {
              -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: res_id_max is ', res_id_max);
    --        res_id_max := 0;
              while (res0_id <= res_id_max) fetch c0 into res0_id;
              -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: res0_id is ', res0_id);
    	  if (res0_id > res_id_max) res_id_max := res0_id;
              while (res1_id < res_id_max) fetch c1 into res1_id;
              -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: res1_id is ', res1_id);
    	  if (res1_id > res_id_max) res_id_max := res1_id;
              while (res2_id < res_id_max) fetch c2 into res2_id;
              -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: res2_id is ', res2_id);
    	  if (res2_id > res_id_max) res_id_max := res2_id;
              if ((res0_id = res_id_max) and (res1_id = res_id_max) and (res2_id = res_id_max))
                {
                  -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: put hit ', res_id_max);
                  dict_put (hit_ids, res_id_max, 1);
                }
            }
        }
    
      p3_id := filter_data [12+1];
      p3_val := "CatFilter_ENCODE_CATVALUE" (filter_data [12+2]);
      if (filter_length = 18) -- distinct propvals with 4 fixed property
        {
          whenever not found goto get_distincts_3;
          open c0 (prefetch 1);
          open c1 (prefetch 1);
          open c2 (prefetch 1);
          open c3 (prefetch 1);
          while (1)
            {
              while (res0_id <= res_id_max) fetch c0 into res0_id;
    	  if (res0_id > res_id_max) res_id_max := res0_id;
              while (res1_id < res_id_max) fetch c1 into res1_id;
    	  if (res1_id > res_id_max) res_id_max := res1_id;
              while (res2_id < res_id_max) fetch c2 into res2_id;
    	  if (res2_id > res_id_max) res_id_max := res2_id;
              while (res3_id < res_id_max) fetch c3 into res3_id;
    	  if (res3_id > res_id_max) res_id_max := res3_id;
              if ((res0_id = res_id_max) and (res1_id = res_id_max) and (res2_id = res_id_max) and (res3_id = res_id_max))
                {
                  -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: put hit ', res_id_max);
                  dict_put (hit_ids, res_id_max, 1);
                }
            }
        }
    
      p4_id := filter_data [16+1];
      p4_val := "CatFilter_ENCODE_CATVALUE" (filter_data [16+2]);
      if (filter_length = 22) -- distinct propvals with 5 fixed property
        {
          whenever not found goto get_distincts_4;
          open c0 (prefetch 1);
          open c1 (prefetch 1);
          open c2 (prefetch 1);
          open c3 (prefetch 1);
          open c4 (prefetch 1);
          while (1)
            {
              while (res0_id <= res_id_max) fetch c0 into res0_id;
    	  if (res0_id > res_id_max) res_id_max := res0_id;
              while (res1_id < res_id_max) fetch c1 into res1_id;
    	  if (res1_id > res_id_max) res_id_max := res1_id;
              while (res2_id < res_id_max) fetch c2 into res2_id;
    	  if (res2_id > res_id_max) res_id_max := res2_id;
              while (res3_id < res_id_max) fetch c3 into res3_id;
    	  if (res3_id > res_id_max) res_id_max := res3_id;
              while (res4_id < res_id_max) fetch c4 into res4_id;
    	  if (res4_id > res_id_max) res_id_max := res4_id;
              if ((res0_id = res_id_max) and (res1_id = res_id_max) and (res2_id = res_id_max) and (res3_id = res_id_max) and (res4_id = res_id_max))
                dict_put (hit_ids, res_id_max, 1);
            }
        }
    
    get_distincts_4:
      -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: close c4');
      close c4;
    get_distincts_3:
      -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: close c3');
      close c3;
    get_distincts_2:
      -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: close c2');
      close c2;
    get_distincts_1:
      -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: close c1');
      close c1;
    get_distincts_0:
      -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: close c0');
      close c0;
    
      -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: now search in all values of ', plast_id);
      whenever not found goto nf_c_last2;
      open c_last2 (prefetch 1);
      while (1)
        {
          fetch c_last2 into res_last_id, v_last;
          if (v_max is null or (v_last > v_max))
            {
              -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: next value ', v_last, ' at ', res_last_id);
              if (dict_get (hit_ids, res_last_id, 0))
                {
                  -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_DISTVALS: full hit at ', res_last_id);
                  v_max := v_last; -- note that vectorbld_acc() will destroy the value of v_last so this assignment should be before vectorbld_acc().
                  dict_put (distval_dict, v_last, 1);
                }
            }
        }
    nf_c_last2:
          close c_last2;
    }
    ;
    
    
    create function "CatFilter_GET_RDF_INVERSE_HITS_RES_IDS" (in cfc_id integer, inout filter_data any, in auth_uid integer) returns any
    {
      declare filter_length, p0_id, p1_id, p2_id, p3_id, p4_id, res0_id, res1_id, res2_id, res3_id, res4_id, res_id_max integer;
      declare acc any;
      declare p0_val, p1_val, p2_val, p3_val, p4_val varchar;
      declare acl_bits any;
      declare auth_gid integer;
      declare c0 cursor for select DRI_RES_ID from WS.WS.SYS_DAV_RDF_INVERSE where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = p0_id and DRI_CATVALUE = p0_val and DRI_RES_ID >= res_id_max and
        exists (select top 1 1 from WS.WS.SYS_DAV_RES where RES_ID = DRI_RES_ID and case (DAV_CHECK_PERM (RES_PERMS, '1__', auth_uid, auth_gid, RES_GROUP, RES_OWNER)) when 0 then WS.WS.ACL_IS_GRANTED (RES_ACL, auth_uid, acl_bits) else 1 end);
      declare c1 cursor for select DRI_RES_ID from WS.WS.SYS_DAV_RDF_INVERSE where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = p1_id and DRI_CATVALUE = p1_val and DRI_RES_ID >= res_id_max;
      declare c2 cursor for select DRI_RES_ID from WS.WS.SYS_DAV_RDF_INVERSE where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = p2_id and DRI_CATVALUE = p2_val and DRI_RES_ID >= res_id_max;
      declare c3 cursor for select DRI_RES_ID from WS.WS.SYS_DAV_RDF_INVERSE where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = p3_id and DRI_CATVALUE = p3_val and DRI_RES_ID >= res_id_max;
      declare c4 cursor for select DRI_RES_ID from WS.WS.SYS_DAV_RDF_INVERSE where DRI_CATF_ID = cfc_id and DRI_PROP_CATID = p4_id and DRI_CATVALUE = p4_val and DRI_RES_ID >= res_id_max;
      -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_RES_IDS (', cfc_id, filter_data, auth_uid, ')');
      filter_length := length (filter_data);
      vectorbld_init (acc);
    
      res0_id := -1;
      res1_id := -1;
      res2_id := -1;
      res3_id := -1;
      res4_id := -1;
      res_id_max := 0;
    
      auth_gid := coalesce ((select U_GROUP from WS.WS.SYS_DAV_USER where U_ID = auth_uid), 0);
      acl_bits := DAV_REQ_CHARS_TO_BITMASK ('1__');
    
      p0_id := filter_data [1];
      p0_val := "CatFilter_ENCODE_CATVALUE" (filter_data [2]);
      if (filter_length = 4) -- resources with 1 fixed property
        {
          whenever not found goto get_distincts_0;
          open c0 (prefetch 1);
          while (1)
            {
              while (res0_id <= res_id_max)
                fetch c0 into res0_id;
              res_id_max := res0_id;
              -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_RES_IDS: put hit ', res_id_max);
              vectorbld_acc (acc, res0_id);
            }
        }
    
      p1_id := filter_data [4+1];
      p1_val := "CatFilter_ENCODE_CATVALUE" (filter_data [4+2]);
      if (filter_length = 8) -- resources with 2 fixed properties
        {
          whenever not found goto get_distincts_1;
          open c0 (prefetch 1);
          open c1 (prefetch 1);
          while (1)
            {
              while (res1_id < res_id_max) fetch c1 into res1_id;
              if (res1_id > res_id_max) res_id_max := res1_id;
              while (res0_id < res_id_max) fetch c0 into res0_id;
              if (res0_id > res_id_max) res_id_max := res0_id;
              if ((res0_id = res_id_max) and (res1_id = res_id_max))
                {
                  -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_RES_IDS: put hit ', res_id_max);
    	      vectorbld_acc (acc, res0_id);
                  res_id_max := res_id_max + 1;
                }
            }
        }
    
      p2_id := filter_data [8+1];
      p2_val := "CatFilter_ENCODE_CATVALUE" (filter_data [8+2]);
      if (filter_length = 12) -- resources with 3 fixed properties
        {
          whenever not found goto get_distincts_2;
          open c0 (prefetch 1);
          open c1 (prefetch 1);
          open c2 (prefetch 1);
          while (1)
            {
              while (res1_id < res_id_max) fetch c1 into res1_id;
    	  if (res1_id > res_id_max) res_id_max := res1_id;
              while (res2_id < res_id_max) fetch c2 into res2_id;
    	  if (res2_id > res_id_max) res_id_max := res2_id;
              while (res0_id < res_id_max) fetch c0 into res0_id;
    	  if (res0_id > res_id_max) res_id_max := res0_id;
              if ((res0_id = res_id_max) and (res1_id = res_id_max) and (res2_id = res_id_max))
                {
                  -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_RES_IDS: put hit ', res_id_max);
    	      vectorbld_acc (acc, res0_id);
                  res_id_max := res_id_max + 1;
                }
            }
        }
    
      p3_id := filter_data [12+1];
      p3_val := "CatFilter_ENCODE_CATVALUE" (filter_data [12+2]);
      if (filter_length = 16) -- resources with 4 fixed properties
        {
          whenever not found goto get_distincts_3;
          open c0 (prefetch 1);
          open c1 (prefetch 1);
          open c2 (prefetch 1);
          open c3 (prefetch 1);
          while (1)
            {
              while (res1_id < res_id_max) fetch c1 into res1_id;
    	  if (res1_id > res_id_max) res_id_max := res1_id;
              while (res2_id < res_id_max) fetch c2 into res2_id;
    	  if (res2_id > res_id_max) res_id_max := res2_id;
              while (res3_id < res_id_max) fetch c3 into res3_id;
    	  if (res3_id > res_id_max) res_id_max := res3_id;
              while (res0_id < res_id_max) fetch c0 into res0_id;
    	  if (res0_id > res_id_max) res_id_max := res0_id;
              if ((res0_id = res_id_max) and (res1_id = res_id_max) and (res2_id = res_id_max) and (res3_id = res_id_max))
                {
                  -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_RES_IDS: put hit ', res_id_max);
    	      vectorbld_acc (acc, res0_id);
                  res_id_max := res_id_max + 1;
                }
            }
        }
    
      p4_id := filter_data [16+1];
      p4_val := "CatFilter_ENCODE_CATVALUE" (filter_data [16+2]);
      if (filter_length = 20) -- resources with 5 fixed properties
        {
          whenever not found goto get_distincts_4;
          open c0 (prefetch 1);
          open c1 (prefetch 1);
          open c2 (prefetch 1);
          open c3 (prefetch 1);
          open c4 (prefetch 1);
          while (1)
            {
              while (res1_id < res_id_max) fetch c1 into res1_id;
    	  if (res1_id > res_id_max) res_id_max := res1_id;
              while (res2_id < res_id_max) fetch c2 into res2_id;
    	  if (res2_id > res_id_max) res_id_max := res2_id;
              while (res3_id < res_id_max) fetch c3 into res3_id;
    	  if (res3_id > res_id_max) res_id_max := res3_id;
              while (res4_id < res_id_max) fetch c4 into res4_id;
    	  if (res4_id > res_id_max) res_id_max := res4_id;
              while (res0_id < res_id_max) fetch c0 into res0_id;
    	  if (res0_id > res_id_max) res_id_max := res0_id;
              if ((res0_id = res_id_max) and (res1_id = res_id_max) and (res2_id = res_id_max) and (res3_id = res_id_max) and (res4_id = res_id_max))
                {
                  -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_RES_IDS: put hit ', res_id_max);
    	      vectorbld_acc (acc, res0_id);
                  res_id_max := res_id_max + 1;
                }
            }
        }
    
    get_distincts_4:
      close c4;
    get_distincts_3:
      close c3;
    get_distincts_2:
      close c2;
    get_distincts_1:
      close c1;
    get_distincts_0:
      close c0;
    
    finalize:
      vectorbld_final (acc);
      -- dbg_obj_princ ('CatFilter_GET_RDF_INVERSE_HITS_RES_IDS (', cfc_id, filter_data, auth_uid, ') returns ', acc);
      return acc;
    }
    ;
    
    
    create function "CatFilter_LIST_PROP_DISTVALS_AUX" (inout dict any, inout rfp varchar, inout vals any)
    {
      -- dbg_obj_princ ('CatFilter_LIST_PROP_DISTVALS_AUX will store ', length (vals), 'values for ', rfp);
      foreach (any val in vals) do
        {
          -- dbg_obj_princ ('CatFilter_LIST_PROP_DISTVALS_AUX will store ', xpath_eval('..', val));
          dict_put (dict, "CatFilter_ENCODE_CATVALUE" (cast (val as varchar)), 1);
        }
      return 1;
    }
    ;
    
    create function "CatFilter_LIST_PROP_DISTVALS" (in detcol_id integer, in cfc_id integer, in rfc_spath varchar, inout rfc_list_cond any, inout schema_uri varchar, inout filter_data any, in auth_uid integer) returns any
    {
      declare prop_catnames, filter, res any;
    --  declare compilation any;
      declare len, ctr integer;
      declare execstate, execmessage varchar;
      declare execmeta, execrows any;
      declare qry_ft, qry_where, qry_text varchar;
    
      declare pred_metas, cmp_metas, table_metas any;
      declare used_tables any;
      declare dict any;
      declare auth_gid integer;
    
      -- dbg_obj_princ ('CatFilter_LIST_PROP_DISTVALS (', cfc_id, rfc_spath, rfc_list_cond, schema_uri, filter_data, auth_uid, ')');
      dict := dict_new ();
    
      if ((length (get_keyword ('', rfc_list_cond)) = 0) and (length (filter_data) > 0) and (length (filter_data) <= 22))
        { -- Optimized merge intersection on inverse hits
          "CatFilter_GET_RDF_INVERSE_HITS_DISTVALS" (cfc_id, filter_data, dict, auth_uid);
          goto plain_resources_passed;
        }
    
      len := length (filter_data);
      vectorbld_init (filter);
      "CatFilter_ACC_FILTER_DATA" (filter, filter_data);
      vectorbld_concat_acc (filter, get_keyword ('', rfc_list_cond));
      vectorbld_final (filter);
    
      -- dbg_obj_princ ('DAV_FC_PRINT_WHERE (', filter, auth_uid, ')');
      DAV_FC_PRED_METAS (pred_metas);
      DAV_FC_CMP_METAS (cmp_metas);
      DAV_FC_TABLE_METAS (table_metas);
      qry_ft := sprintf ('virt:rdf/virt:top-res/virt:prop[*[1][self::(!%s!)]]/virt:value', filter_data [len-2]);
      used_tables := vector (
        'SYS_DAV_RES', vector ('SYS_DAV_RES', '_top', null, vector(), vector(), vector()),
        'SYS_DAV_PROP, PROP_NAME=http://local.virt/DAV-RDF', vector ('SYS_DAV_PROP', '_rdf', '(_rdf.PROP_NAME = ''http://local.virt/DAV-RDF'')', vector(), vector(), vector('[' || qry_ft || ']'))
        );
      qry_where := DAV_FC_PRINT_WHERE_INT (filter, pred_metas, cmp_metas, table_metas, used_tables,
        coalesce ((select COL_OWNER from WS.WS.SYS_DAV_COL where COL_ID = detcol_id), -1) );
    
      auth_gid := coalesce ((select U_GROUP from WS.WS.SYS_DAV_USER where U_ID = auth_uid), 0);
    
    --  compilation := vector ('', filter, 'DAV', DAV_FC_PRINT_WHERE (filter, auth_uid));
      qry_text := '
    select count ( "CatFilter_LIST_PROP_DISTVALS_AUX" (?, _top.RES_FULL_PATH,
      xpath_eval (''[xmlns:virt="virt"] /' || qry_ft ||''',
        xml_tree_doc (deserialize (cast (_rdf.PROP_VALUE as varchar))),
        0 ) ) )
    from WS.WS.SYS_DAV_RES as _top
    ' || qry_where || ' and
      (_top.RES_FULL_PATH between ' || WS.WS.STR_SQL_APOS (rfc_spath) || ' and ' || WS.WS.STR_SQL_APOS (DAV_COL_PATH_BOUNDARY (rfc_spath)) || ') and
      case (DAV_CHECK_PERM (_top.RES_PERMS, ''1__'', ?, ?, _top.RES_GROUP, _top.RES_OWNER)) when 0 then WS.WS.ACL_IS_GRANTED (_top.RES_ACL, ?, DAV_REQ_CHARS_TO_BITMASK (''1__'')) else 1 end
    ';
          -- dbg_obj_princ ('about to exec:\n', qry_text, '\nrfc_spath = ', rfc_spath);
      exec (qry_text,
        execstate, execmessage, vector (dict, auth_uid, auth_gid, auth_uid), 1, execmeta, execrows );
      -- dbg_obj_princ ('execstate = ', execstate, ' execmessage = ', execmessage);
    
    plain_resources_passed:
      for
        select CFD_DET_SUBCOL_ID, CFD_DET from WS.WS.SYS_DAV_CATFILTER_DETS where CFD_CF_ID = cfc_id
      do
        {
          if (exists (select top 1 1 from SYS_PROCEDURES where P_NAME = fix_identifier_case('DB.DBA.') || CFD_DET || '_CF_LIST_PROP_DISTVALS'))
            call (CFD_DET || '_CF_LIST_PROP_DISTVALS') (CFD_DET_SUBCOL_ID, cfc_id, rfc_spath, rfc_list_cond, schema_uri, filter_data, dict, auth_uid);
        }
      return dict_list_keys (dict, 1);
    }
    ;
    
    create function "CatFilter_DAV_DIR_LIST" (in detcol_id any, in path_parts any, in detcol_path varchar, in name_mask varchar, in recursive integer, in auth_uid integer) returns any
    {
      declare cfc_id integer;
      declare rfc_spath varchar;
      declare rfc_list_cond, rfc_del_action any;
      declare davpath, prev_raw_name, schema_uri, subcol_perms varchar;
      declare depth integer;
      declare res, resources, itm, reps, filter_data any;
      declare ctr, itm_ctr, itm_count, prev_is_patched, set_readonly integer;
      declare filter any;
      -- dbg_obj_princ ('CatFilter_DAV_DIR_LIST (', detcol_id, path_parts, detcol_path, name_mask, recursive, auth_uid, ')');
      vectorbld_init (res);
      filter_data := null;
      if (0 > "CatFilter_GET_CONDITION" (detcol_id, cfc_id, rfc_spath, rfc_list_cond, rfc_del_action))
        {
          -- dbg_obj_princ ('broken filter - no items');
          goto final_res;
        }
      subcol_perms := coalesce ((select COL_PERMS from WS.WS.SYS_DAV_COL where COL_ID = detcol_id), '000000000N');
      subcol_perms[2] := 48; subcol_perms[5] := 48; subcol_perms[8] := 48; -- Can't execute in CatFilter so place zero chars.
      if (1 < length(path_parts))
        {
          if ("CatFilter_PATH_PARTS_TO_FILTER" (path_parts, schema_uri, filter_data) < 0)
            {
              -- dbg_obj_princ ('"CatFilter_DAV_DIR_LIST" ends due to failed "CatFilter_PATH_PARTS_TO_FILTER"');
              goto final_res;
            }
        }
      else
        filter_data := null;
      set_readonly := 0;
      if (length (rfc_del_action) < length (rfc_list_cond))
        {
          -- dbg_obj_princ ('del_action = ', rfc_del_action, 'rfc_list_cond = ', rfc_list_cond);
          set_readonly := 1;
        }
      else
        if (-1 = recursive)
          {
            if ((2 = length(path_parts)) or (mod (length (filter_data), 4) = 2))
              set_readonly := 1;
          }
        else
          {
            if ((1 = length(path_parts)) or (mod (length (filter_data), 4) = 0))
              set_readonly := 1;
          }
      if (set_readonly)
        {
          subcol_perms[1] := 48; subcol_perms[4] := 48; subcol_perms[7] := 48; -- Can't write in CatFilter so place zero chars.
        }
      depth := length(path_parts);
    -- level 0 -- schemas;
      if (1 = length(path_parts))
        {
          declare schemas any;
          -- dbg_obj_princ ('level of list of schemas');
          if ('' <> path_parts[0])
            {
              -- dbg_obj_princ ('no resources at level 0');
              return vector();
            }
          schemas := "CatFilter_LIST_SCHEMAS" (rfc_spath, rfc_list_cond, auth_uid);
          foreach (any sch in schemas) do
            {
              declare subcol_fullpath varchar;
              subcol_fullpath := DAV_CONCAT_PATH (detcol_path, sch[1] || '/');
              vectorbld_acc (res,
                vector (subcol_fullpath, 'C', 0, now (),
    	      vector (UNAME'CatFilter', detcol_id, null, sch[0], null),
    	      subcol_perms, 0, auth_uid, now (), 'dav/unix-directory', sch[1]) );
              if (recursive > 0)
                vectorbld_concat_acc (res,
                  "CatFilter_DAV_DIR_LIST" (detcol_id,
                     vector_concat (subseq (path_parts, 0, length (path_parts) - 1), vector (sch[1], '')),
                     detcol_path, -- not subcol_fullpath,
                     name_mask, recursive, auth_uid ) );
            }
          goto final_res;
        }
      if ("CatFilter_PATH_PARTS_TO_FILTER" (path_parts, schema_uri, filter_data) < 0)
        {
          -- dbg_obj_princ ('"CatFilter_DAV_DIR_LIST" ends due to failed "CatFilter_PATH_PARTS_TO_FILTER"');
          goto final_res;
        }
      -- dbg_obj_princ ('"CatFilter_DAV_DIR_LIST" founds schema_uri = ', schema_uri, ' filter_data = ', filter_data);
    -- We crop at 5 property values, i.e. 20 items in filter data. Sixth property is not displayed to keep URI short.
      if (mod (length (filter_data), 4) = 2)
        { -- list distinct values at odd levels
          declare distvals any;
          -- dbg_obj_princ ('level of distinct values');
          distvals := "CatFilter_LIST_PROP_DISTVALS" (detcol_id, cfc_id, rfc_spath, rfc_list_cond, schema_uri, filter_data, auth_uid);
          if (-1 = recursive)
            {
              --if (0 = length (distvals))
              --  return vector();
              return vector (
                vector (DAV_CONCAT_PATH (detcol_path, path_parts), 'C', 0, now (),
    	      vector (UNAME'CatFilter', detcol_id, null, schema_uri, filter_data),
    	      subcol_perms, 0, auth_uid, now (), 'dav/unix-directory', path_parts [depth - 2] ) );
            }
          foreach (varchar val in distvals) do
            {
              declare subcol_fullpath varchar;
              subcol_fullpath := DAV_CONCAT_PATH ( DAV_CONCAT_PATH (detcol_path, path_parts), val || '/');
              vectorbld_acc (res,
                vector (subcol_fullpath, 'C', 0, now (),
    	      vector (UNAME'CatFilter', detcol_id, null, schema_uri, vector_concat (filter_data, vector (val))),
    	      subcol_perms, 0, auth_uid, now (), 'dav/unix-directory', val) );
              if (recursive > 0)
                vectorbld_concat_acc (res,
                  "CatFilter_DAV_DIR_LIST" (detcol_id,
                     vector_concat (subseq (path_parts, 0, length (path_parts) - 1), vector (val, '')),
                     detcol_path, -- not subcol_fullpath,
                     name_mask, recursive, auth_uid ) );
            }
          goto final_res;
        }
      else if (length (filter_data) <= 16)
        {
          declare sch_props any;
          -- dbg_obj_princ ('level of prop list');
          sch_props := "CatFilter_LIST_SCHEMA_PROPS" (rfc_spath, rfc_list_cond, schema_uri, filter_data, auth_uid);
          if (-1 = recursive)
            {
              --if (0 = length (sch_props))
              --  return vector();
              return vector (
                vector (DAV_CONCAT_PATH (detcol_path, path_parts), 'C', 0, now (),
    	      vector (UNAME'CatFilter', detcol_id, null, schema_uri, filter_data),
    	      subcol_perms, 0, auth_uid, now (), 'dav/unix-directory', path_parts [depth - 2] ) );
            }
    -- The 'if' below disables infinite recursion.
    -- All resources will be displayed, but not all subcollections.
    -- This is the longest possible finite list CatFilter can offer to the application.
          if (length (filter_data) >= 4)
            recursive := 0;
          foreach (any prop in sch_props) do
            {
              declare subcol_fullpath varchar;
              subcol_fullpath := DAV_CONCAT_PATH (DAV_CONCAT_PATH (detcol_path, path_parts), prop[1] || '/');
              vectorbld_acc (res,
                vector (subcol_fullpath, 'C', 0, now (),
    	      vector (UNAME'CatFilter', detcol_id, null, prop[0], null),
    	      subcol_perms, 0, auth_uid, now (), 'dav/unix-directory', prop[1]) );
              if (recursive > 0)
                vectorbld_concat_acc (res,
                  "CatFilter_DAV_DIR_LIST" (detcol_id,
                     vector_concat (subseq (path_parts, 0, length (path_parts) - 1), vector (prop[1], '')),
                     detcol_path, -- not subcol_fullpath,
                     name_mask, recursive, auth_uid ) );
            }
        }
      -- dbg_obj_princ ('res = ', res);
      if (0 = length (filter_data))
        {
          -- dbg_obj_princ ('no resources with 0 filter data len');
          goto final_res; -- otherwise all files are listed from all schemas.
        }
    
      if ((length (get_keyword ('', rfc_list_cond)) = 0) and (length (filter_data) > 0) and (length (filter_data) <= 20))
        { -- Optimized merge intersection on inverse hits
          declare res_ids, res_dir_single any;
          res_ids := "CatFilter_GET_RDF_INVERSE_HITS_RES_IDS" (cfc_id, filter_data, auth_uid);
          -- dbg_obj_princ ('resources = ', res_ids);
          itm_count := length (res_ids);
          vectorbld_init (resources);
          for (itm_ctr := 0; itm_ctr < itm_count; itm_ctr := itm_ctr + 1)
            {
              declare r_id integer;
              r_id := res_ids [itm_ctr];
              res_dir_single := coalesce ((
    	    select
    --                    0                                        1    2                     3
                  vector (DAV_CONCAT_PATH (detcol_path, RES_NAME), 'R', length (RES_CONTENT), RES_MOD_TIME,
    --              4     5          6          7          8            9         10
    	        r_id, RES_PERMS, RES_GROUP, RES_OWNER, RES_CR_TIME, RES_TYPE, RES_NAME )
    	    from WS.WS.SYS_DAV_RES
    	    where RES_ID = r_id ) );
    	  if (res_dir_single is not null)
    	    vectorbld_acc (resources, res_dir_single);
            }
          for select CFD_DET_SUBCOL_ID, CFD_DET from WS.WS.SYS_DAV_CATFILTER_DETS where CFD_CF_ID = cfc_id do
            {
              declare det_res_ids any;
              if (exists (select top 1 1 from SYS_PROCEDURES where P_NAME = fix_identifier_case('DB.DBA.') || CFD_DET || '_CF_GET_RDF_HITS'))
                {
                  det_res_ids := call (CFD_DET || '_CF_GET_RDF_HITS') (CFD_DET_SUBCOL_ID, cfc_id, rfc_spath, rfc_list_cond, schema_uri, filter_data, detcol_path, 1, auth_uid);
    	      vectorbld_concat_acc (resources, det_res_ids);
                }
            }
          vectorbld_final (resources);
        }
      else
        {
          vectorbld_init (filter);
          "CatFilter_ACC_FILTER_DATA" (filter, filter_data);
          vectorbld_concat_acc (filter, get_keyword ('', rfc_list_cond));
          -- dbg_obj_princ ('name_mask = ', name_mask);
          if ('%' <> name_mask)
            {
              -- dbg_obj_princ ('Adding check for name mask');
              vectorbld_acc (filter, vector ('RES_NAME', 'like', name_mask));
            }
          vectorbld_final (filter);
          filter := vector ('', filter);
          resources := DAV_DIR_FILTER_INT (rfc_spath, 1, filter, null, null, auth_uid);
        }
      reps := dict_new ();
      itm_count := length (resources);
      for (itm_ctr := 0; itm_ctr < itm_count; itm_ctr := itm_ctr + 1)
        {
          declare rname varchar;
          declare orig_id any;
          itm := resources [itm_ctr];
          rname := itm [10];
          orig_id := itm[4];
          if (isarray (orig_id) or regexp_parse ('^([^/][^./]*) -Rf((Id[1-9][0-9]*)|([A-Z][A-Za-z0-9]+)-([A-Za-z0-9~+-]*))([.][^/]*)?\044', rname, 0)) -- Suspicious names should be qualified
            resources [itm_ctr][10] := rname := "ResFilter_FNMERGE" (rname, orig_id);
          dict_put (reps, rname, dict_get (reps, rname, 0) + 1);
        }
      for (itm_ctr := 0; itm_ctr < itm_count; itm_ctr := itm_ctr + 1)
        {
          declare rname varchar;
          declare orig_id integer;
          itm := resources [itm_ctr];
          rname := itm [10];
          orig_id := itm[4];
          resources[itm_ctr][4] := vector (UNAME'CatFilter', detcol_id, orig_id);
          if (dict_get (reps, rname, 0) > 1) -- Suspicious names should be qualified
            resources [itm_ctr][10] := rname := "ResFilter_FNMERGE" (rname, orig_id);
          resources[itm_ctr][0] := DAV_CONCAT_PATH (DAV_CONCAT_PATH (detcol_path, path_parts), rname);
        }
      vectorbld_concat_acc (res, resources);
      -- dbg_obj_princ ('res = ', res);
    
    final_res:
      vectorbld_final (res);
      -- dbg_obj_princ ('\nCatFilter_DAV_DIR_LIST (', detcol_id, path_parts, detcol_path, name_mask, recursive, auth_uid, ') returns ', length (res), ' items:\n');
      -- foreach (any i in res) do -- dbg_obj_princ (i);
      return res;
    }
    ;
    
    
    create function "CatFilter_DAV_DIR_FILTER" (in detcol_id any, in path_parts any, in detcol_path varchar, inout compilation any, in recursive integer, in auth_uid integer) returns any
    {
      declare cfc_id integer;
      declare rfc_spath varchar;
      declare rfc_list_cond, rfc_del_action any;
      declare davpath, prev_raw_name varchar;
      declare res, itm, reps any;
      declare itm_ctr, itm_count, prev_is_patched integer;
      -- dbg_obj_princ ('CatFilter_DAV_DIR_FILTER (', detcol_id, path_parts, detcol_path, compilation, recursive, auth_uid, ')');
      if (0 > "CatFilter_GET_CONDITION" (detcol_id, cfc_id, rfc_spath, rfc_list_cond, rfc_del_action))
        {
          -- dbg_obj_princ ('broken filter - no items');
          return vector();
        }
      if (0 = length (get_keyword ('', compilation)))
        res := DAV_DIR_FILTER_INT (rfc_spath, 1, rfc_list_cond, null, null, auth_uid);
      else
        {
          declare tmp_cond any;
          tmp_cond := vector ('',
            vector_concat (
              get_keyword ('', compilation),
              get_keyword ('', rfc_list_cond) ) );
          res := DAV_DIR_FILTER_INT (rfc_spath, 1, tmp_cond, null, null, auth_uid);
        }
      reps := dict_new ();
      itm_count := length (res);
      for (itm_ctr := 0; itm_ctr < itm_count; itm_ctr := itm_ctr + 1)
        {
          declare rname varchar;
          declare orig_id integer;
          itm := res [itm_ctr];
          rname := itm [10];
          orig_id := itm[4];
          if (isarray (orig_id) or regexp_parse ('^([^/][^./]*) -Rf((Id[1-9][0-9]*)|([A-Z][A-Za-z0-9]+)-([A-Za-z0-9~+-]*))([.][^/]*)?\044', rname, 0)) -- Suspicious names should be qualified
            res [itm_ctr][10] := rname := "ResFilter_FNMERGE" (rname, orig_id);
          dict_put (reps, rname, dict_get (reps, rname, 0) + 1);
        }
      for (itm_ctr := 0; itm_ctr < itm_count; itm_ctr := itm_ctr + 1)
        {
          declare rname varchar;
          declare orig_id integer;
          itm := res [itm_ctr];
          rname := itm [10];
          orig_id := itm[4];
          res[itm_ctr][4] := vector (UNAME'CatFilter', detcol_id, orig_id);
          if (dict_get (reps, rname, 0) > 1) -- Suspicious names should be qualified
            res [itm_ctr][10] := rname := "ResFilter_FNMERGE" (rname, orig_id);
          res[itm_ctr][0] := DAV_CONCAT_PATH (DAV_CONCAT_PATH (detcol_path, path_parts), rname);
        }
      return res;
    }
    ;
    
    
    create function "CatFilter_DAV_SEARCH_ID" (in detcol_id any, in path_parts any, in what char(1)) returns any
    {
      declare cfc_id integer;
      declare rfc_spath varchar;
      declare rfc_list_cond, rfc_del_action any;
      declare orig_id, filter_data any;
      -- dbg_obj_princ ('CatFilter_DAV_SEARCH_ID (', detcol_id, path_parts, what, ')');
      rfc_spath := null;
      orig_id := "CatFilter_DAV_SEARCH_ID_IMPL" (detcol_id, path_parts, what, cfc_id, rfc_spath, rfc_list_cond, rfc_del_action, filter_data);
      return orig_id;
    }
    ;
    
    
    create function "CatFilter_DAV_SEARCH_PATH" (in id any, in what char(1)) returns any
    {
      -- dbg_obj_princ ('CatFilter_DAV_SEARCH_PATH (', id, what, ')');
      if ('R' = what)
        return coalesce ((select RES_FULL_PATH from WS.WS.SYS_DAV_RES where RES_ID = id[2]), null);
      if ('C' = what)
        {
          declare res varchar;
          res := DAV_SEARCH_PATH (id[1], 'C');
          if (id[3] is not null)
            {
            -- TBD
              ;
            }
          if (id[4] is not null)
            {
            -- TBD
              ;
            }
          return res;
        }
    
      return -14;
    }
    ;
    
    
    create function "CatFilter_DAV_RES_UPLOAD_COPY" (in detcol_id any, in path_parts any, in source_id any, in what char(1), in overwrite integer, in permissions varchar, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      declare cfc_id integer;
      declare rfc_spath, schema_uri varchar;
      declare rfc_list_cond, rfc_del_action, filter_data, fit_cond any;
      declare rc integer;
      -- dbg_obj_princ ('CatFilter_DAV_RES_UPLOAD_COPY (', detcol_id, path_parts, source_id, what, overwrite, permissions, uid, gid, auth_uid, ')');
      if (0 > "CatFilter_GET_CONDITION" (detcol_id, cfc_id, rfc_spath, rfc_list_cond, rfc_del_action))
        {
          -- dbg_obj_princ ('broken filter - no items');
          return -2;
        }
      if (length (rfc_del_action) < length (rfc_list_cond))
        {
          -- dbg_obj_princ ('del_action = ', rfc_del_action, 'rfc_list_cond = ', rfc_list_cond);
          return -13;
        }
      rc := "CatFilter_PATH_PARTS_TO_FILTER" (path_parts, schema_uri, filter_data);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      rc := "CatFilter_FILTER_TO_CONDITION" (schema_uri, filter_data, fit_cond);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      fit_cond := vector ('', vector_concat (fit_cond, get_keyword ('', rfc_list_cond)));
      if ('R' <> what)
        return -2;
      if ('' = path_parts [length (path_parts) - 1])
        return -2;
      if (isinteger (source_id) and
         exists (select 1 from WS.WS.SYS_DAV_RES
             where RES_ID = source_id and RES_NAME = path_parts [length (path_parts) - 1] and (RES_FULL_PATH between rfc_spath and DAV_COL_PATH_BOUNDARY (rfc_spath)) ) )
        {
          "ResFilter_FIT_INTO_CONDITION" (source_id, what, fit_cond, auth_uid);
        }
      else
        {
          declare new_full_path varchar;
          new_full_path := DAV_CONCAT_PATH (rfc_spath, path_parts [length (path_parts) - 1]);
          rc := DAV_COPY_INT (DAV_SEARCH_PATH (source_id, what), new_full_path, overwrite, permissions,
             coalesce ((select U_NAME from WS.WS.SYS_DAV_USER where U_ID = uid), ''),
             coalesce ((select G_NAME from WS.WS.SYS_DAV_GROUP where G_ID = gid), ''),
             null, null, 0);
          if (DAV_HIDE_ERROR (rc) is null)
            return rc;
          source_id := DAV_SEARCH_ID (new_full_path, what);
          if (DAV_HIDE_ERROR (source_id) is null)
            return source_id;
          if (not (isinteger (source_id)))
            return -13;
          "ResFilter_FIT_INTO_CONDITION" (source_id, what, fit_cond, auth_uid);
        }
      return 1;
    }
    ;
    
    
    create function "CatFilter_DAV_RES_UPLOAD_MOVE" (in detcol_id any, in path_parts any, in source_id any, in what char(1), in overwrite integer, in auth_uid integer) returns any
    {
      declare cfc_id integer;
      declare rfc_spath, schema_uri varchar;
      declare rfc_list_cond, rfc_del_action, filter_data, fit_cond any;
      declare rc integer;
      -- dbg_obj_princ ('CatFilter_DAV_RES_UPLOAD_MOVE (', detcol_id, path_parts, source_id, what, overwrite, auth_uid, ')');
      if (0 > "CatFilter_GET_CONDITION" (detcol_id, cfc_id, rfc_spath, rfc_list_cond, rfc_del_action))
        {
          -- dbg_obj_princ ('broken filter - no items');
          return -2;
        }
      if (length (rfc_del_action) < length (rfc_list_cond))
        {
          -- dbg_obj_princ ('del_action = ', rfc_del_action, 'rfc_list_cond = ', rfc_list_cond);
          return -13;
        }
      rc := "CatFilter_PATH_PARTS_TO_FILTER" (path_parts, schema_uri, filter_data);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      rc := "CatFilter_FILTER_TO_CONDITION" (schema_uri, filter_data, fit_cond);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      fit_cond := vector ('', vector_concat (fit_cond, get_keyword ('', rfc_list_cond)));
      if ('R' <> what)
        return -2;
      if ('' = path_parts [length (path_parts) - 1])
        return -2;
      if (isinteger (source_id) and
        exists (select 1 from WS.WS.SYS_DAV_RES
            where RES_ID = source_id and RES_NAME = path_parts [length (path_parts) - 1] and (RES_FULL_PATH between rfc_spath and DAV_COL_PATH_BOUNDARY (rfc_spath))))
        {
          "ResFilter_FIT_INTO_CONDITION" (source_id, what, fit_cond, auth_uid);
        }
      else
        {
          declare new_full_path varchar;
          new_full_path := DAV_CONCAT_PATH (rfc_spath, path_parts [length (path_parts) - 1]);
          rc := DAV_MOVE_INT (DAV_SEARCH_PATH (source_id, what), new_full_path, overwrite, null, null, 0, 1);
          if (DAV_HIDE_ERROR (rc) is null)
            return rc;
          source_id := DAV_SEARCH_ID (new_full_path, what);
          if (DAV_HIDE_ERROR (source_id) is null)
            return source_id;
          if (not (isinteger (source_id)))
            return -13;
          "ResFilter_FIT_INTO_CONDITION" (source_id, what, fit_cond, auth_uid);
        }
      return 1;
    }
    ;
    
    
    create function "CatFilter_DAV_RES_CONTENT" (in id any, inout content any, out type varchar, in content_mode integer) returns integer
    {
      -- dbg_obj_princ ('CatFilter_DAV_RES_CONTENT (', id, ', [content], [type], ', content_mode, ')');
      declare cont any;
      if ((content_mode = 0) or (content_mode = 2))
        select RES_CONTENT, RES_TYPE into content, type from WS.WS.SYS_DAV_RES where RES_ID = id[2];
      else if (content_mode = 1)
        select http (RES_CONTENT, content), RES_TYPE into cont, type from WS.WS.SYS_DAV_RES where RES_ID = id[2];
      else if (content_mode = 3)
        select http (RES_CONTENT), RES_TYPE into cont, type from WS.WS.SYS_DAV_RES where RES_ID = id[2];
      return id[2];
    }
    ;
    
    
    create function "CatFilter_DAV_SYMLINK" (in detcol_id any, in path_parts any, in source_id any, in what char(1), in overwrite integer, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('CatFilter_DAV_SYMLINK (', detcol_id, path_parts, source_id, overwrite, uid, gid, auth_uid, ')');
      return -20;
    }
    ;
    
    
    create function "CatFilter_DAV_LOCK" (in path any, inout id any, in type char(1), inout locktype varchar, inout scope varchar, in token varchar, inout owner_name varchar, inout owned_tokens varchar, in depth varchar, in timeout_sec integer, in auth_uid integer) returns any
    {
      declare rc, u_token, new_token varchar;
      -- dbg_obj_princ ('CatFilter_DAV_LOCK (', path, id, type, locktype, scope, token, owner_name, owned_tokens, depth, timeout_sec, auth_uid, ')');
      if ('R' <> type)
        return -20;
      if (DAV_HIDE_ERROR (id) is null)
        return -20;
      if (isarray (id))
        return DAV_LOCK_INT (path, id[2], type, locktype, scope, token, owner_name, owned_tokens, depth, timeout_sec, null, null, auth_uid);
      return -20;
    }
    ;
    
    
    create function "CatFilter_DAV_UNLOCK" (in id any, in type char(1), in token varchar, in auth_uid integer)
    {
      -- dbg_obj_princ ('CatFilter_DAV_UNLOCK (', id, type, token, auth_uid, ')');
      if (isarray (id))
        id := id [2];
      return DAV_UNLOCK_INT (id, type, token, null, null, auth_uid);
    }
    ;
    
    
    create function "CatFilter_DAV_IS_LOCKED" (inout id any, inout type char(1), in owned_tokens varchar) returns integer
    {
      declare rc integer;
      declare orig_id any;
      declare orig_type char(1);
      -- dbg_obj_princ ('CatFilter_DAV_IS_LOCKED (', id, type, owned_tokens, ')');
      orig_id := id;
      id := orig_id[2];
      rc := DAV_IS_LOCKED_INT (id, type, owned_tokens);
      if (rc <> 0)
        return rc;
      id := orig_id[1];
      orig_type := type;
      type := 'C';
      rc := DAV_IS_LOCKED_INT (id, type, owned_tokens);
      if (rc <> 0)
        return rc;
      id := orig_id;
      type := orig_type;
      return 0;
    }
    ;
    
    
    create function "CatFilter_DAV_LIST_LOCKS" (in id any, in type char(1), in recursive integer) returns any
    {
      declare res any;
      -- dbg_obj_princ ('CatFilter_DAV_LIST_LOCKS" (', id, type, recursive);
      id := id[2];
      if (isarray (id))
        return call (cast (id[0] as varchar) || '_DAV_LIST_LOCKS') (id, type, recursive);
      res := vector();
      for select LOCK_TYPE, LOCK_SCOPE, LOCK_TOKEN, LOCK_TIMEOUT, LOCK_OWNER, LOCK_OWNER_INFO
        from WS.WS.SYS_DAV_LOCK where LOCK_PARENT_ID = id and LOCK_PARENT_TYPE = type do {
          res := vector_concat (res, vector (vector (LOCK_TYPE, LOCK_SCOPE, LOCK_TOKEN, LOCK_TIMEOUT, LOCK_OWNER, LOCK_OWNER_INFO)));
        }
      return res;
    }
    ;
    
    
    create function "CatFilter_CONFIGURE" (in col any, in search_path varchar, in filter any, in auth_uname varchar := 'dav', in auth_upwd varchar := 'dav', in auth_uid integer := null) returns integer
    {
      declare cfid, rc, ctr integer;
      declare colname varchar;
      declare compilation, del_act any;
      compilation := vector ('', filter);
      rc := DAV_DIR_FILTER_INT (search_path, 1, compilation, auth_uname, auth_upwd, auth_uid);
      if (isinteger (rc))
        return rc;
      if (not isinteger (col))
        return -20;
      colname := DAV_SEARCH_PATH (col, 'C');
      if (not (isstring (colname)))
        return -23;
      rc := DAV_SEARCH_ID (search_path, 'C');
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      if (search_path <> DAV_SEARCH_PATH (rc, 'C'))
        return -2;
      if (search_path between colname and (colname || '\255\255\255\255'))
        return -28;
      rc := DAV_PROP_SET_INT (colname, 'virt:ResFilter-SearchPath', search_path, null, null, 0, 1, 1);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      rc := DAV_PROP_SET_INT (colname, 'virt:ResFilter-ListCond', "ResFilter_ENCODE_FILTER" (compilation), null, null, 0, 1, 1);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      del_act := "ResFilter_MAKE_DEL_ACTION_FROM_CONDITION" (compilation);
      -- dbg_obj_princ ('ResFilter_CONFIGURE has made del_action ', del_act, ' from ', compilation);
      rc := DAV_PROP_SET_INT (colname, 'virt:ResFilter-DelAction', "ResFilter_ENCODE_FILTER" (del_act), null, null, 0, 1, 1);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
    
      cfid := coalesce ((select CF_ID from WS.WS.SYS_DAV_CATFILTER where CF_SEARCH_PATH = search_path));
      if (cfid is null)
        {
          declare search_path_z varchar;
          cfid := WS.WS.GETID ('CF');
          insert into WS.WS.SYS_DAV_CATFILTER (CF_ID, CF_SEARCH_PATH) values (cfid, search_path);
          search_path_z := search_path || '\255\255\255\255';
          for (select p.PROP_VALUE, p.PROP_PARENT_ID
            from WS.WS.SYS_DAV_RES r join WS.WS.SYS_DAV_PROP p on (r.RES_ID = p.PROP_PARENT_ID)
            where (r.RES_FULL_PATH between search_path and search_path_z) and (p.PROP_NAME = 'http://local.virt/DAV-RDF') and (p.PROP_TYPE = 'R')) do
    	{
    	  "CatFilter_FEED_DAV_RDF_INVERSE" (PROP_VALUE, PROP_PARENT_ID, 0, cfid);
    	  ctr := ctr + 1;
    	  if (mod (ctr, 1000) = 0)
    	    commit work;
    	}
          commit work;
          for (select COL_ID, COL_DET, WS.WS.COL_PATH (COL_ID) as _c_path from WS.WS.SYS_DAV_COL where COL_DET is not null and not (COL_DET like '%Filter')) do
            {
              if ("LEFT" (_c_path, length (search_path)) = search_path)
    	    {
                  insert replacing WS.WS.SYS_DAV_CATFILTER_DETS (CFD_CF_ID, CFD_DET_SUBCOL_ID, CFD_DET)
    	      values (cfid, COL_ID, COL_DET);
        	    }
    	}
        }
    --  if (search_path between colname and (colname || '\255\255\255\255'))
    --    return -28;
    --  if (colname between search_path and (search_path || '\255\255\255\255'))
    --    return -28;
      rc := DAV_PROP_SET_INT (colname, 'virt:CatFilter-ID', cast (cfid as varchar), null, null, 0, 1, 1);
      if (DAV_HIDE_ERROR (rc) is null)
        return rc;
      update WS.WS.SYS_DAV_COL set COL_DET='CatFilter' where COL_ID=col;
    
      return 0;
    }
    ;
    
    
    create procedure "CatFilter_FEED_DAV_RDF_INVERSE" (inout propval any, inout propparent integer, in is_del integer := 0, in cfid integer := null)
    {
      declare resfullpath, path_head, pv varchar;
      declare doc any;
      declare triplets any;
    
      if (126 = __tag (propval))
        pv := blob_to_string (propval);
      else
        {
          if ((not isstring (propval)) or (propval = ''))
    	return;
          pv := propval;
        }
      if (193 <> pv[0])
        return;
      doc := null;
      if (cfid is not null)
        {
          path_head := '/';
          goto cfid_found;
        }
      else
        {
          resfullpath := coalesce ((select r.RES_FULL_PATH from WS.WS.SYS_DAV_RES r where r.RES_ID = propparent));
          if (resfullpath is null)
            return;
          path_head := subseq (resfullpath, 0, strrchr (resfullpath, '/'));
        }
    
    next_cfid:
      while (1)
        {
          if (length (path_head) <= 1)
            return;
          cfid := coalesce ((select CF_ID from WS.WS.SYS_DAV_CATFILTER where CF_SEARCH_PATH = (path_head || '/')));
          path_head := subseq (path_head, 0, strrchr (path_head, '/'));
          if (cfid is not null)
            goto cfid_found;
        }
    
    cfid_found:
      if (doc is null)
        {
          doc := deserialize (pv);
          if (0 = length (doc))
            return;
          doc := xml_tree_doc (doc);
        }
      -- dbg_obj_princ ('CatFilter_INS_DAV_RDF_INVERSE: saving res ', propparent, ' in catfilter ', cfid);
      triplets := xpath_eval ('[xmlns:virt="virt"] /virt:rdf/virt:top-res/virt:prop[virt:value]', doc, 0);
      foreach (any prop in triplets) do
        {
          declare propname varchar;
          declare prop_catid integer;
          propname := cast (xpath_eval ('name(*[1])', prop) as varchar);
          prop_catid := coalesce ((select RPN_CATID from WS.WS.SYS_RDF_PROP_NAME where RPN_URI = propname));
          if (prop_catid is null)
            {
              prop_catid := WS.WS.GETID ('RPN');
              -- dbg_obj_princ ('CatFilter_INS_DAV_RDF_INVERSE: insert into WS.WS.SYS_RDF_PROP_NAME (RPN_URI, RPN_CATID) values (', propname, prop_catid, ')');
              insert into WS.WS.SYS_RDF_PROP_NAME (RPN_URI, RPN_CATID) values (propname, prop_catid);
            }
          if (is_del)
            delete from WS.WS.SYS_DAV_RDF_INVERSE
            where
              (DRI_CATF_ID = cfid) and (DRI_PROP_CATID = prop_catid) and
              (DRI_CATVALUE = "CatFilter_ENCODE_CATVALUE" (cast (xpath_eval ('[xmlns:virt="virt"] virt:value', prop) as varchar))) and
              (DRI_RES_ID = propparent);
          else
            insert soft WS.WS.SYS_DAV_RDF_INVERSE (DRI_CATF_ID, DRI_PROP_CATID, DRI_CATVALUE, DRI_RES_ID)
            values (
              cfid,
              prop_catid,
              "CatFilter_ENCODE_CATVALUE" (cast (xpath_eval ('[xmlns:virt="virt"] virt:value', prop) as varchar)),
              propparent );
        }
      goto next_cfid;
    }
    ;
    
    
    create trigger SYS_DAV_PROP_VALUE_RDF_I after insert on WS.WS.SYS_DAV_PROP order 10 referencing new as NP
    {
      if (NP.PROP_NAME <> 'http://local.virt/DAV-RDF')
        return;
      if (NP.PROP_TYPE <> 'R')
        return;
      "CatFilter_FEED_DAV_RDF_INVERSE" (NP.PROP_VALUE, NP.PROP_PARENT_ID);
    }
    ;
    
    
    create trigger SYS_DAV_PROP_VALUE_RDF_D before delete on WS.WS.SYS_DAV_PROP order 10 referencing old as OP
    {
      declare pv varchar;
      declare doc any;
      if (OP.PROP_NAME <> 'http://local.virt/DAV-RDF')
        return;
      if (OP.PROP_TYPE <> 'R')
        return;
      "CatFilter_FEED_DAV_RDF_INVERSE" (OP.PROP_VALUE, OP.PROP_PARENT_ID, 1);
    }
    ;
    
    
    create trigger SYS_DAV_PROP_VALUE_RDF_U after update on WS.WS.SYS_DAV_PROP order 10 referencing old as OP, new as NP
    {
      declare pv varchar;
      declare doc any;
      if (OP.PROP_NAME <> 'http://local.virt/DAV-RDF')
        goto register_new_propvals;
      if (OP.PROP_TYPE <> 'R')
        goto register_new_propvals;
      "CatFilter_FEED_DAV_RDF_INVERSE" (OP.PROP_VALUE, OP.PROP_PARENT_ID, 1);
    
    register_new_propvals:
      if (NP.PROP_NAME <> 'http://local.virt/DAV-RDF')
        return;
      if (NP.PROP_TYPE <> 'R')
        return;
      "CatFilter_FEED_DAV_RDF_INVERSE" (NP.PROP_VALUE, NP.PROP_PARENT_ID);
    }
    ;
    
    
    create procedure "CatFilter_INIT_SYS_DAV_RDF_INVERSE" (in run_if_once integer)
    {
      declare ctr integer;
      set isolation = 'committed';
      if (run_if_once)
        {
          if (0 <> sequence_next('CatFilter_INIT_SYS_DAV_RDF_INVERSE'))
            return;
        }
      else
        {
    -- For safety, schemas should be recompiled.
          update WS.WS.SYS_RDF_SCHEMAS set RS_PRECOMPILED = null, RS_PROP_CATNAMES = null;
          commit work;
          delete from WS.WS.SYS_DAV_RDF_INVERSE;
        }
      commit work;
      for (select PROP_VALUE, PROP_PARENT_ID from WS.WS.SYS_DAV_PROP where PROP_NAME = 'http://local.virt/DAV-RDF' and PROP_TYPE = 'R') do
        {
          "CatFilter_FEED_DAV_RDF_INVERSE" (PROP_VALUE, PROP_PARENT_ID);
          ctr := ctr + 1;
          if (mod (ctr, 1000) = 0)
            commit work;
        }
    }
    ;