-- function check_representation
create or replace function acs_group__check_representation (integer)
returns boolean as '
declare
  group_id               alias for $1;  
  res                    boolean; 
  comp                   record;
  memb                   record;      
begin
   if group_id is null then 
        --maybe we should just return ''f'' instead?
	raise exception ''acs_group__check_representation called with null group_id'';
   end if;

   res := ''t'';
   PERFORM acs_log__notice(''acs_group.check_representation'',
                  ''Running check_representation on group '' || group_id);

   if acs_object__check_representation(group_id) = ''f'' then
     res := ''f'';
   end if;

   for comp in select c.rel_id
             from acs_rels r, composition_rels c
             where r.rel_id = c.rel_id
             and r.object_id_one = group_id 
   LOOP
     if composition_rel__check_representation(comp.rel_id) = ''f'' then
       res := ''f'';
     end if;
   end loop;

   for memb in  select m.rel_id
             from acs_rels r, membership_rels m
             where r.rel_id = m.rel_id
             and r.object_id_one = group_id 
   LOOP
     if membership_rel__check_representation(memb.rel_id) = ''f'' then
       res := ''f'';
     end if;
   end loop;

   PERFORM acs_log__notice(''acs_group.check_representation'',
                  ''Done running check_representation on group '' || group_id);

   return res;
  
end;' language 'plpgsql';

-- check for null input raise exception.

create or replace function acs_object__check_context_index (integer,integer,integer)
returns boolean as '
declare
  check_context_index__object_id              alias for $1;  
  check_context_index__ancestor_id            alias for $2;  
  check_context_index__n_generations          alias for $3;  
  n_rows                                      integer;       
  n_gens                                      integer;       
begin
   -- Verify that this row exists in the index.
   if check_context_index__object_id is null or check_context_index__ancestor_id is null then
	raise exception ''object_id or ancestor_id is null in acs_object__check_context_index'';
   end if;	
   select case when count(*) = 0 then 0 else 1 end into n_rows
   from acs_object_context_index
   where object_id = check_context_index__object_id
   and ancestor_id = check_context_index__ancestor_id;

   if n_rows = 1 then
     -- Verify that the count is correct.
     select n_generations into n_gens
     from acs_object_context_index
     where object_id = check_context_index__object_id
     and ancestor_id = check_context_index__ancestor_id;

     if n_gens != check_context_index__n_generations then
       PERFORM acs_log__error(''acs_object.check_representation'', 
                              ''Ancestor '' ||
                     check_context_index__ancestor_id || '' of object '' || 
                     check_context_index__object_id ||
		     '' reports being generation '' || n_gens ||
		     '' when it is actually generation '' || 
                     check_context_index__n_generations ||
		     ''.'');
       return ''f'';
     else
       return ''t'';
     end if;
   else
     PERFORM acs_log__error(''acs_object.check_representation'', 
                            ''Ancestor '' ||
                            check_context_index__ancestor_id || 
                            '' of object '' || check_context_index__object_id 
                            || '' is missing an entry in acs_object_context_index.'');
     return ''f'';
   end if;
  
end;' language 'plpgsql';


-- function check_path
create or replace function acs_object__check_path (integer,integer)
returns boolean as '
declare
  check_path__object_id              alias for $1;  
  check_path__ancestor_id            alias for $2;  
  check_path__context_id             acs_objects.context_id%TYPE;
  check_path__security_inherit_p     acs_objects.security_inherit_p%TYPE;
begin
   if check_path__object_id is null or check_path__ancestor_id then 
	raise exception ''acs_object__check_path called with null object_id or ancestor_id'';
   end if;
   if check_path__object_id = check_path__ancestor_id then
     return ''t'';
   end if;

   select context_id, security_inherit_p 
   into check_path__context_id, check_path__security_inherit_p
   from acs_objects
   where object_id = check_path__object_id;

   -- we should be able to handle the case where check_path fails 
   -- should we not?

   if check_path__object_id = 0 and check_path__context_id is null then 
      return ''f'';
   end if;

   if check_path__context_id is null or check_path__security_inherit_p = ''f'' 
   then
     check_path__context_id := 0;
   end if;

   return acs_object__check_path(check_path__context_id, 
                                 check_path__ancestor_id);
  
end;' language 'plpgsql';



create or replace function acs_object__check_representation (integer)
returns boolean as '
declare
  check_representation__object_id              alias for $1;  
  result                                       boolean;       
  check_representation__object_type            acs_objects.object_type%TYPE;
  n_rows                                       integer;    
  v_rec                                        record;  
  row                                          record; 
begin
   if check_representation__object_id is null then 
	raise exception ''acs_object__check_representation called for null object_id'';
   end if;

   result := ''t'';
   PERFORM acs_log__notice(''acs_object.check_representation'',
                  ''Running acs_object.check_representation on object_id = '' 
                  || check_representation__object_id || ''.'');

   select object_type into check_representation__object_type
   from acs_objects
   where object_id = check_representation__object_id;

   PERFORM acs_log__notice(''acs_object.check_representation'',
                  ''OBJECT STORAGE INTEGRITY TEST'');

   for v_rec in  select t.object_type, t.table_name, t.id_column
             from acs_object_type_supertype_map m, acs_object_types t
	     where m.ancestor_type = t.object_type
	     and m.object_type = check_representation__object_type
	     union
	     select object_type, table_name, id_column
	     from acs_object_types
	     where object_type = check_representation__object_type 
     LOOP

        for row in execute ''select case when count(*) = 0 then 0 else 1 end as n_rows from '' || quote_ident(v_rec.table_name) || '' where '' || quote_ident(v_rec.id_column) || '' = '' || check_representation__object_id
        LOOP
            n_rows := row.n_rows;
            exit;
        end LOOP;

        if n_rows = 0 then
           result := ''f'';
           PERFORM acs_log__error(''acs_object.check_representation'',
                     ''Table '' || v_rec.table_name || 
                     '' (primary storage for '' ||
		     v_rec.object_type || 
                     '') doesn''''t have a row for object '' ||
		     check_representation__object_id || '' of type '' || 
                     check_representation__object_type || ''.'');
        end if;

   end loop;

   PERFORM acs_log__notice(''acs_object.check_representation'',
                  ''OBJECT CONTEXT INTEGRITY TEST'');

   if acs_object__check_object_ancestors(check_representation__object_id, 
                                         check_representation__object_id, 0) = ''f'' then
     result := ''f'';
   end if;

   if acs_object__check_object_descendants(check_representation__object_id, 
                                           check_representation__object_id, 0) = ''f'' then
     result := ''f'';
   end if;
   for row in  select object_id, ancestor_id, n_generations
	       from acs_object_context_index
	       where object_id = check_representation__object_id
	       or ancestor_id = check_representation__object_id 
   LOOP
     if acs_object__check_path(row.object_id, row.ancestor_id) = ''f'' then
       PERFORM acs_log__error(''acs_object.check_representation'',
		     ''acs_object_context_index contains an extraneous row: ''
                     || ''object_id = '' || row.object_id || 
                     '', ancestor_id = '' || row.ancestor_id || 
                     '', n_generations = '' || row.n_generations || ''.'');
       result := ''f'';
     end if;
   end loop;

   PERFORM acs_log__notice(''acs_object.check_representation'',
		  ''Done running acs_object.check_representation '' || 
		  ''on object_id = '' || check_representation__object_id || ''.'');

   return result;
  
end;' language 'plpgsql';


create or replace function acs_object__get_attr_storage_column(text) 
returns text as '
declare
        v_vals  alias for $1;
        v_idx   integer;
begin
        v_idx := strpos(v_vals,'','');
        if v_idx = 0 or v_vals is null then 
           raise exception ''invalid storage format: acs_object.get_attr_storage_column %'',v_vals;
        end if;

        return substr(v_vals,1,v_idx - 1);

end;' language 'plpgsql' immutable;

create or replace function acs_object__get_attr_storage_table(text) 
returns text as '
declare
        v_vals  alias for $1;
        v_idx   integer;
        v_tmp   varchar;
begin
        v_idx := strpos(v_vals,'','');

        if v_idx = 0 or v_vals is null then 
           raise exception ''invalid storage format: acs_object.get_attr_storage_table %'',v_vals;
        end if;

        v_tmp := substr(v_vals,v_idx + 1);
        v_idx := strpos(v_tmp,'','');
        if v_idx = 0 then 
           raise exception ''invalid storage format: acs_object.get_attr_storage_table %'',v_vals;
        end if;

        return substr(v_tmp,1,v_idx - 1);

end;' language 'plpgsql' immutable;

create or replace function acs_object__get_attr_storage_sql(text) 
returns text as '
declare
        v_vals  alias for $1;
        v_idx   integer;
        v_tmp   varchar;
begin
        v_idx := strpos(v_vals, '','');

        if v_idx = 0 or v_vals is null then 
           raise exception ''invalid storage format: acs_object.get_attr_storage_sql %'',v_vals;
        end if;

        v_tmp := substr(v_vals, v_idx + 1);
        v_idx := strpos(v_tmp, '','');
        if v_idx = 0 then 
           raise exception ''invalid storage format: acs_object.get_attr_storage_sql %'',v_vals;
        end if;

        return substr(v_tmp, v_idx + 1);

end;' language 'plpgsql' immutable;



create or replace function acs_object__get_attribute_storage (integer,varchar)
returns text as '
declare
  object_id_in           alias for $1;  
  attribute_name_in      alias for $2;  

--  these three are the out variables
  v_column               varchar;  
  v_table_name           varchar;  
  v_key_sql              text;
  
  v_object_type          acs_attributes.object_type%TYPE;
  v_static               acs_attributes.static_p%TYPE;
  v_attr_id              acs_attributes.attribute_id%TYPE;
  v_storage              acs_attributes.storage%TYPE;
  v_attr_name            acs_attributes.attribute_name%TYPE;
  v_id_column            varchar(200);   
  v_sql                  text;  
  v_return               text;  
  v_rec                  record;
begin
   --   select 
   --     object_type, id_column
   --   from
   --     acs_object_types
   --   connect by
   --     object_type = prior supertype
   --   start with
   --     object_type = (select object_type from acs_objects 
   --                    where object_id = object_id_in)

   -- Determine the attribute parameters
   select
     a.attribute_id, a.static_p, a.storage, a.table_name, a.attribute_name,
     a.object_type, a.column_name, t.id_column 
   into 
     v_attr_id, v_static, v_storage, v_table_name, v_attr_name, 
     v_object_type, v_column, v_id_column
   from 
     acs_attributes a,
     (select o2.object_type, o2.id_column
       from acs_object_types o1, acs_object_types o2
      where o1.object_type = (select object_type
                                from acs_objects o
                               where o.object_id = object_id_in)
        and o1.tree_sortkey between o2.tree_sortkey and tree_right(o2.tree_sortkey)
     ) t
   where   
     a.attribute_name = attribute_name_in
   and
     a.object_type = t.object_type;

   if NOT FOUND then 
      raise EXCEPTION ''-20000: No such attribute % for object % in acs_object.get_attribute_storage.'', attribute_name_in, object_id_in;
   end if;

   -- This should really be done in a trigger on acs_attributes,
   -- instead of generating it each time in this function

   -- If there is no specific table name for this attribute,
   -- figure it out based on the object type
   if v_table_name is null or v_table_name = '''' then

     -- Determine the appropriate table name
     if v_storage = ''generic'' then
       -- Generic attribute: table name/column are hardcoded

       v_column := ''attr_value'';

       if v_static = ''f'' then
         v_table_name := ''acs_attribute_values'';
         v_key_sql := ''(object_id = '' || object_id_in || '' and '' ||
                      ''attribute_id = '' || v_attr_id || '')'';
       else
         v_table_name := ''acs_static_attr_values'';
         v_key_sql := ''(object_type = '''''' || v_object_type || '''''' and '' ||
                      ''attribute_id = '' || v_attr_id || '')'';
       end if;

     else
       -- Specific attribute: table name/column need to be retreived
 
       if v_static = ''f'' then
         select 
           table_name, id_column 
         into 
           v_table_name, v_id_column
         from 
           acs_object_types 
         where 
           object_type = v_object_type;
         if NOT FOUND then 
            raise EXCEPTION ''-20000: No data found for attribute %::% object_id % in acs_object.get_attribute_storage'', v_object_type, attribute_name_in, object_id_in;
         end if;
       else
         raise EXCEPTION ''-20000: No table name specified for storage specific static attribute %::% object_id % in acs_object.get_attribute_storage.'',v_object_type, attribute_name_in, object_id_in;
       end if;
  
     end if;
   else 
     -- There is a custom table name for this attribute.
     -- Get the id column out of the acs_object_tables
     -- Raise an error if not found
     select id_column into v_id_column from acs_object_type_tables
       where object_type = v_object_type 
       and table_name = v_table_name;
       if NOT FOUND then 
          raise EXCEPTION ''-20000: No data found for attribute %::% object_id % in acs_object.get_attribute_storage'', v_object_type, attribute_name_in, object_id_in;
       end if;
   end if;

   if v_column is null or v_column = '''' then

     if v_storage = ''generic'' then
       v_column := ''attr_value'';
     else
       v_column := v_attr_name;
     end if;

   end if;

   if v_key_sql is null or v_key_sql = '''' then
     if v_static = ''f'' then   
       v_key_sql := v_id_column || '' = '' || object_id_in ; 
     else
       v_key_sql := v_id_column || '' = '''''' || v_object_type || '''''''';
     end if;
   end if;

   return v_column || '','' || v_table_name || '','' || v_key_sql; 

end;' language 'plpgsql' strict;


create or replace function acs_object__initialize_attributes (integer)
returns integer as '
declare
  initialize_attributes__object_id              alias for $1;  
  v_object_type                                 acs_objects.object_type%TYPE;
begin
   if  initialize_attributes__object_id is null then 
	raise exception ''acs_object__initialize_attributes called with null object_id'';
   end if;

   -- Initialize dynamic attributes
   insert into acs_attribute_values
    (object_id, attribute_id, attr_value)
   select
    initialize_attributes__object_id, a.attribute_id, a.default_value
   from acs_attributes a, acs_objects o
   where a.object_type = o.object_type
   and o.object_id = initialize_attributes__object_id
   and a.storage = ''generic''
   and a.static_p = ''f'';

   -- Retrieve type for static attributes
   select object_type into v_object_type from acs_objects
     where object_id = initialize_attributes__object_id;

   -- Initialize static attributes
   -- begin
     insert into acs_static_attr_values
      (object_type, attribute_id, attr_value)
     select
      v_object_type, a.attribute_id, a.default_value
     from acs_attributes a, acs_objects o
     where a.object_type = o.object_type
       and o.object_id = initialize_attributes__object_id
       and a.storage = ''generic''
       and a.static_p = ''t''
       and not exists (select 1 from acs_static_attr_values
                       where object_type = a.object_type);
   -- exception when no_data_found then null;

   return 0; 
end;' language 'plpgsql';



create or replace function acs_object__set_attribute (integer,varchar,varchar)
returns integer as '
declare
  object_id_in           alias for $1;  
  attribute_name_in      alias for $2;  
  value_in               alias for $3;  
  v_table_name           varchar;  
  v_column               varchar;  
  v_key_sql              text; 
  v_return               text; 
  v_storage              text;
begin
   if value_in is null then
	-- this will fail more cryptically in the execute so catch now. 
	raise exception ''acs_object__set_attribute: attempt to set % to null for object_id %'',attribute_name_in, object_id_in;
   end if;

   v_storage := acs_object__get_attribute_storage(object_id_in, attribute_name_in);

   v_column     := acs_object__get_attr_storage_column(v_storage);
   v_table_name := acs_object__get_attr_storage_table(v_storage);
   v_key_sql    := acs_object__get_attr_storage_sql(v_storage);

   execute ''update '' || v_table_name || '' set '' || quote_ident(v_column) || '' = '' || quote_literal(value_in) || '' where '' || v_key_sql;

   return 0; 
end;' language 'plpgsql';


create or replace function acs_object_util__get_object_type (integer)
returns varchar as '
declare
    p_object_id         alias for $1;
    v_object_type       varchar(100);
begin
    select object_type into v_object_type
    from acs_objects
    where object_id = p_object_id;

    if not found then
        raise exception ''acs_object_util__get_object_type: Invalid Object id: % '', p_object_id;
    end if;

    return v_object_type;

end;' language 'plpgsql' stable;

create or replace function acs_objects_get_tree_sortkey(integer) returns varbit as '
declare
  p_object_id    alias for $1;
begin
  return tree_sortkey from acs_objects where object_id = p_object_id;
end;' language 'plpgsql' stable strict;

create or replace function acs_rel_type__drop_type (varchar,boolean)
returns integer as '
declare
  drop_type__rel_type               alias for $1;  
  drop_type__cascade_p              alias for $2;  -- default ''f''  
  v_cascade                         boolean;
begin
    -- XXX do cascade_p.
    -- JCD: cascade_p seems to be ignored in acs_o_type__drop_type anyway...
    if drop_type__cascade_p is null then 
	v_cascade_p := ''f'';
    else 
	v_cascade_p := drop_type__cascade_p;
    end if;

    delete from acs_rel_types
	  where rel_type = drop_type__rel_type;

    PERFORM acs_object_type__drop_type(drop_type__rel_type, 
                                       v_cascade_p);

    return 0; 
end;' language 'plpgsql';


create or replace function apm__unregister_package (varchar,boolean)
returns integer as '
declare
  package_key            alias for $1;  
  cascade_p              alias for $2;  -- default ''t''  
begin
   if cascade_p is null then 
	cascade_p := ''t'';
   end if;

   PERFORM apm_package_type__drop_type(
	package_key,
	cascade_p
   );

   return 0; 
end;' language 'plpgsql';


create or replace function apm__unregister_service (varchar,boolean)
returns integer as '
declare
  package_key            alias for $1;  
  cascade_p              alias for $2;  -- default ''f''  
begin
   if cascade_p is null then 
	cascade_p := ''f'';
   end if;

   PERFORM apm__unregister_package (
	package_key,
	cascade_p
   );

   return 0; 
end;' language 'plpgsql';

create or replace function apm_package__num_instances (varchar) returns integer as '
declare
        num_instances__package_key     alias for $1;
        v_num_instances                integer;
begin
        select count(*) into v_num_instances
	from apm_packages
	where package_key = num_instances__package_key;

        return v_num_instances;

end;' language 'plpgsql' stable;



create or replace function lob_copy(integer, integer) returns integer as '
declare
        from_id         alias for $1;
        to_id           alias for $2;
begin
	if from_id is null then 
	    raise exception ''lob_copy: attempt to copy null from_id to % to_id'',to_id;
        end if;

        insert into lobs (lob_id,refcount) values (to_id,0);

        insert into lob_data
             select to_id as lob_id, segment, byte_len, data
               from lob_data
              where lob_id = from_id;

        return null;

end;' language 'plpgsql';


create or replace function apm_package__parent_id (integer) returns integer as '
declare
    apm_package__parent_id__package_id alias for $1;
    v_package_id apm_packages.package_id%TYPE;
begin
    select sn1.object_id
    into v_package_id
    from site_nodes sn1
    where sn1.node_id = (select sn2.parent_id
                         from site_nodes sn2
                         where sn2.object_id = apm_package__parent_id__package_id);

    return v_package_id;

end;' language 'plpgsql' stable strict;

create or replace function apm_package__singleton_p (varchar) returns integer as '
declare
	singleton_p__package_key        alias for $1;
        v_singleton_p                   integer;
begin
        select count(*) > 0 into v_singleton_p
	from apm_package_types
	where package_key = singleton_p__package_key
        and singleton_p = ''t'';

        return v_singleton_p;
end;' language 'plpgsql' stable;

create or replace function number_src(text) returns text as '
declare
        v_src   alias for $1;
        v_pos   integer;
        v_ret   text default '''';
        v_tmp   text;
        v_cnt   integer default -1;
begin
        if v_src is null then 
	     return null;
        end if;
        v_tmp := v_src;
        LOOP
            v_pos := position(''\n'' in v_tmp);
            v_cnt := v_cnt + 1;

            exit when v_pos = 0;

            if v_cnt != 0 then
              v_ret := v_ret || rpad(v_cnt,10) || substr(v_tmp,1,v_pos);
            end if;
            v_tmp := substr(v_tmp,v_pos + 1);
        end LOOP;

        return v_ret || rpad(v_cnt,10) || v_tmp;

end;' language 'plpgsql' immutable strict;

create or replace function party__email (integer)
returns varchar as '
declare
  email__party_id		alias for $1;
begin

  return email from parties where party_id = email__party_id;

end;' language 'plpgsql' stable strict;

create or replace function person__first_names (integer)
returns varchar as '
declare
  first_names__person_id        alias for $1;  
begin
  return first_names
  from persons
  where person_id = first_names__person_id;
  
end;' language 'plpgsql' stable strict;


create or replace function person__last_name (integer)
returns varchar as '
declare
  last_name__person_id        alias for $1;  
begin
  return last_name
  from persons
  where person_id = last_name__person_id;

end;' language 'plpgsql' stable strict;


create or replace function person__name (integer)
returns varchar as '
declare
  name__person_id        alias for $1;  
begin

  return first_names || '' '' || last_name
  from persons
  where person_id = name__person_id;

end;' language 'plpgsql';


create or replace function rel_constraint__get_constraint_id (integer,char,integer)
returns integer as '
declare
  get_constraint_id__rel_segment            alias for $1;  
  get_constraint_id__rel_side               alias for $2;  
  get_constraint_id__required_rel_segment   alias for $3;  
  v_constraint_id                           rel_constraints.constraint_id%TYPE;
begin
    return constraint_id
    from rel_constraints
    where rel_segment = get_constraint_id__rel_segment
      and rel_side = get_constraint_id__rel_side
      and required_rel_segment = get_constraint_id__required_rel_segment;

end;' language 'plpgsql' stable strict;

create or replace function rel_segment__get (integer,varchar)
returns integer as '
declare
  get__group_id         alias for $1;  
  get__rel_type         alias for $2;  
begin

   return min(segment_id)
   from rel_segments
   where group_id = get__group_id
     and rel_type = get__rel_type;
  
end;' language 'plpgsql' stable strict;

create or replace function rel_segment__name (integer)
returns varchar as '
declare
  name__segment_id             alias for $1;  
  name__segment_name           varchar(200);  
begin
  return segment_name
  from rel_segments
  where segment_id = name__segment_id;

end;' language 'plpgsql' stable strict;

---- DRB: fixes bug 1144

drop view registered_users;
create view registered_users
as
  select p.email, p.url, pe.first_names, pe.last_name, u.*, mr.member_state
  from parties p, persons pe, users u, group_member_map m, membership_rels mr, acs_magic_objects amo
  where party_id = person_id
  and person_id = user_id
  and u.user_id = m.member_id
  and m.rel_id = mr.rel_id
  and amo.name = 'registered_users'
  and m.group_id = amo.object_id
  and m.container_id = m.group_id
  and m.rel_type = 'membership_rel'
  and mr.member_state = 'approved'
  and u.email_verified_p = 't';


drop view cc_users;
create view cc_users
as
select o.*, pa.*, pe.*, u.*, mr.member_state, mr.rel_id
from acs_objects o, parties pa, persons pe, users u, group_member_map m, membership_rels mr, acs_magic_objects amo
where o.object_id = pa.party_id
  and pa.party_id = pe.person_id
  and pe.person_id = u.user_id
  and u.user_id = m.member_id
  and amo.name = 'registered_users'
  and m.group_id = amo.object_id
  and m.rel_id = mr.rel_id
  and m.container_id = m.group_id
  and m.rel_type = 'membership_rel';

