mikras_utils 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 356975b3f96e8341dba97e96ca170a0fb181c3c8654bc874300144774094daba
4
- data.tar.gz: 5cb71f7c6add1f89ca5025bcc9630a58474055516295e50d9ad6a2386cf6eb84
3
+ metadata.gz: 74256281ee10bf5186723bfa260cd62dda3057216660aa945c7b591fa1581177
4
+ data.tar.gz: 882636fab035922c146cc33c33a5af9deaf0da818bec6897e17a84ad8f1f2289
5
5
  SHA512:
6
- metadata.gz: 74ba9b96d512f831d0dd1a691d1c6892447ac21f29fe91a1f62a5cd87dfefda93d95229c16ddd063ff6bfe3ae1782ea44bda0189eedf7cf757ee7ab5825e9497
7
- data.tar.gz: 1d5a378eddd2900ef31cfcd908bdcdd4f780eaf89a162946bdf445429ef076a8fee43e2b5b7709f3fbc2852f166f43983a4b87064f59574674f64cded967e0de
6
+ metadata.gz: 5ca4be02024c23f2109cc7f3919331c180f6403cb04906d5652446d80241e7b502ed9cfcebfcc5ddf66b098c4b6def8e3f1cd3863942f9e988cb3e5bd1abc501
7
+ data.tar.gz: 6d82b691b1b68214910dac0acc847a68b928b6e0f45c8a157e272ded5fdc84b2f3fe5e3f23ddbadbee4bd82f1621164eea1d736deb7923787fb9f999926ddf0e
data/exe/mkacl CHANGED
@@ -1,13 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  SPEC = %(
4
- @ Make RLS policies
4
+ @ Make RLS functions and policies
5
5
 
6
- -- [DATABASE [USERNAME]] SPEC-FILE
6
+ -- [DATABASE] SPEC-FILE
7
7
 
8
- Read SPEC and generate following functions, policies, and triggers
8
+ Read SPEC and generate following functions, policies, and triggers. The
9
+ default DATABASE and USERNAME variables are read from the environment and
10
+ then .prick.state.yml if present; otherwise the current user's database is
11
+ used
9
12
 
10
- SPEC FILE
13
+ FUNCTIONS
14
+ todo
15
+
16
+ TRIGGERS
17
+ todo
18
+
19
+ SPEC FILE
11
20
  The format is a YAML file where each root key represents a table. A table
12
21
  consists of insert, select, update, and delete actions that are arrays of
13
22
  ACL entries. An ACL entry list of associated roles and possibly a check SQL
@@ -25,17 +34,20 @@ SPEC = %(
25
34
  The following roles are recognized: RLA, LA, TA, KON, AKK, CLA, CTA, ELA,
26
35
  and ETA
27
36
 
28
- Sagsys also defines the ADM role that is excluded because it is now a RBAC
29
- fole, and PUP and NON that are excluded because they are not implemented in
30
- Mikras
37
+ Sagsys also defines the ADM role that is excluded because it is a RBAC role
38
+ in Mikras, and PUP and NON roles that are excluded because they are not
39
+ implemented in Mikras
31
40
 
32
41
  OPTIONS
42
+ -d,dump
43
+ Dump internal format. Used for debugging
44
+
33
45
  -i,interactive
34
46
  Generate code for interactive use by added ON_ERROR_STOP and other variables
35
47
 
36
48
  -g,generate=LIST
37
49
  Generate only the given modules. LIST is a comma-separated list of
38
- modules. The following modules are currently available: id_functions,
50
+ modules. The following modules are currently available: seeds, id_functions,
39
51
  insert_triggers, rules, acl_functions, and role_functions
40
52
 
41
53
  -W,no-warn
@@ -46,14 +58,17 @@ SPEC = %(
46
58
  require 'yaml'
47
59
  require 'shellopts'
48
60
 
61
+ require_relative '../lib/mikras_utils/mikras.rb'
49
62
  require_relative '../lib/mikras_utils/mkacl.rb'
50
63
 
64
+ PRICK_STATE_FILE = "prick.state.yml"
65
+
51
66
  opts, args = ShellOpts::process(SPEC, ARGV)
52
67
  file = args.extract(-1)
53
- database, username = args.extract(0..2)
54
- username ||= database || ENV['PRICK_USERNAME']
55
- database ||= ENV['PRICK_DATABASE']
56
- conn = PgConn.new database, username
68
+ database = args.extract(0..1)
69
+
70
+ conn = PgConn.new *Mikras.credentials(database)
71
+ conn.schema.exist?("prick") or ShellOpts.error "Database '#{database}' is not a prick database"
57
72
 
58
73
  if opts.generate?
59
74
  modules = opts.generate.split(',').map(&:to_sym)
@@ -64,8 +79,10 @@ end
64
79
 
65
80
  spec = MkAcl::Parser.parse(file)
66
81
  MkAcl::Analyzer.analyze(spec, conn, warn: !opts.no_warn?)
67
- #spec.dump
68
- #exit
82
+ if opts.dump?
83
+ spec.dump
84
+ exit
85
+ end
69
86
  MkAcl::Generator.generate(spec, conn, modules, interactive: opts.interactive?)
70
87
 
71
88
 
data/exe/rls ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'indented_io'
4
+ require 'shellopts'
5
+ require 'forward_to'
6
+
7
+ include ForwardTo
8
+
9
+ require_relative '../lib/mikras_utils/rls/spec.rb'
10
+ require_relative '../lib/mikras_utils/rls/parser.rb'
11
+ require_relative '../lib/mikras_utils/rls/analyzer.rb'
12
+
13
+ def error(msg) = ShellOpts.error(msg)
14
+
15
+ SPEC = %(
16
+ Parse RLS spec file
17
+
18
+ -- FILE
19
+ )
20
+
21
+ opts, args = ShellOpts.process(SPEC, ARGV)
22
+
23
+ file = args.expect(1)
24
+ spec = Parser.parse(file)
25
+ Analyzer.analyze(spec)
26
+
27
+ spec.dump
28
+
29
+
30
+
@@ -0,0 +1,24 @@
1
+
2
+ module Mikras
3
+ def self.credentials(database_argument)
4
+ if database_argument
5
+ database = database_argument
6
+ username = database
7
+ else
8
+ username ||= database || ENV['PRICK_USERNAME']
9
+ database ||= ENV['PRICK_DATABASE']
10
+
11
+ if database.nil? && File.exist?(PRICK_STATE_FILE)
12
+ prick_state = YAML.load(IO.read PRICK_STATE_FILE)
13
+ database = prick_state["database"]
14
+ username = prick_state["username"]
15
+ else
16
+ database ||= ENV["USER"]
17
+ username ||= database
18
+ end
19
+ end
20
+ [database, username]
21
+ end
22
+ end
23
+
24
+
@@ -15,19 +15,21 @@ module MkAcl
15
15
  end
16
16
 
17
17
  def analyze
18
+ # Find child-parent relations between linked tables
18
19
  links = conn.tuples %(
19
20
  select
20
- table_name,
21
- ref_table_name,
22
- schema_name || '.' || table_name,
23
- ref_schema_name || '.' || ref_table_name
21
+ table_name as "child_table_name",
22
+ schema_name || '.' || table_name as "child_table_uid",
23
+ ref_table_name as "parent_table_name",
24
+ ref_schema_name || '.' || ref_table_name as "parent_table_uid",
25
+ column_name as "parent_link_field"
24
26
  from meta.links
25
- where schema_name = '#{spec.app_schema}'
27
+ where schema_name = '#{spec.app_schema}'
26
28
  and ref_schema_name = '#{spec.app_schema}'
27
29
  )
28
30
 
29
- # Link up tables
30
- for child_table_name, parent_table_name, child_table_uid, parent_table_uid in links
31
+ # Assign table references
32
+ for child_table_name, child_table_uid, parent_table_name, parent_table_uid, parent_link_field in links
31
33
  if !spec.key?(child_table_name)
32
34
  @uncovered_tables << child_table_name
33
35
  next
@@ -35,8 +37,11 @@ module MkAcl
35
37
  @uncovered_tables << parent_table_name
36
38
  next
37
39
  end
38
-
39
- spec[child_table_name].parents << spec[parent_table_name] if spec[parent_table_name]
40
+
41
+ child_table = spec[child_table_name] or raise "Can't find table #{parent_table_name.inspect}"
42
+ parent_table = spec[parent_table_name] or raise "Can't find referenced table #{parent_table_name.inspect}"
43
+
44
+ child_table.references[parent_table.name] = [parent_table, parent_link_field]
40
45
  end
41
46
 
42
47
  # if warn && !@uncovered_tables.empty?
@@ -44,29 +49,38 @@ module MkAcl
44
49
  # indent { puts uncovered_tables }
45
50
  # end
46
51
 
47
- # Assign domains
48
- spec.tables.each { |table|
49
- resolve_domain(table)
50
- }
51
-
52
- # Check that no table has more than one parent. FIXME
52
+ # Assign parents
53
53
  spec.tables.each { |table|
54
- table.parents.size <= 1 or raise ArgumentError, "Table '#{table.name}' has multiple parents"
54
+ if table.parent_name
55
+ table.parent = spec[table.parent_name] or raise ArgumentError, "Can't find '#{table.parent_name}'"
56
+ table.parent_link_field = table.references[table.parent.name].last
57
+ else
58
+ table.references.size <= 1 or raise ArgumentError, "Table '#{table.name}' has multiple references"
59
+ parent, link_field = table.references.values.first
60
+ if parent&.acl
61
+ table.parent = parent
62
+ table.parent_link_field = link_field
63
+ end
64
+ end
55
65
  }
56
66
 
67
+ # Resolve domains
68
+ spec.tables.select(&:acl).each { |t| resolve_domain(t) }
69
+
57
70
  spec
58
71
  end
59
72
 
60
73
  def self.analyze(spec, conn, **opts) self.new(spec, conn, **opts).analyze end
61
74
 
62
75
  private
63
- def find_tables
64
-
65
- end
66
-
67
76
  def resolve_domain(table)
68
- table.domain ||= table.parents.first && resolve_domain(table.parents.first)
77
+ if table.domain.nil?
78
+ !table.parent.nil? or raise ArgumentError, "Domain table without domain name - '#{table.name}'"
79
+ resolve_domain(table.parent)
80
+ table.domain = table.parent.domain
81
+ end
69
82
  end
70
83
  end
71
84
  end
72
85
 
86
+
@@ -4,10 +4,11 @@ require_relative './generators/acl_functions.rb'
4
4
  require_relative './generators/insert_triggers.rb'
5
5
  require_relative './generators/role_functions.rb'
6
6
  require_relative './generators/rules.rb'
7
+ require_relative './generators/seeds.rb'
7
8
 
8
9
  module MkAcl
9
10
  class Generator
10
- MODULES = [:id_functions, :insert_triggers, :rules, :acl_functions, :role_functions]
11
+ MODULES = [:id_functions, :insert_triggers, :rules, :acl_functions, :role_functions, :seeds]
11
12
 
12
13
  using String::Text
13
14
 
@@ -32,6 +33,7 @@ module MkAcl
32
33
 
33
34
  matches = modules.map { |k| [k, true] }.to_h
34
35
 
36
+ Seeds.generate(self) if matches.key?(:seeds)
35
37
  IdFunctions.generate(self) if matches.key?(:id_functions)
36
38
  for table in spec.tables
37
39
  InsertTriggers.generate(self, table) if matches.key?(:insert_triggers)
@@ -42,7 +44,7 @@ module MkAcl
42
44
  end
43
45
 
44
46
  def self.generate(spec, conn, modules = MODULES, **opts)
45
- self.new(spec, conn).generate(modules, **opts)
47
+ self.new(spec, conn).generate(modules, **opts)
46
48
  end
47
49
 
48
50
  def inspect() "Generator(#{@database.inspect}, #{@username.inspect}, #{@spec})" end
@@ -21,11 +21,28 @@ module MkAcl
21
21
  def self.generate(generator) self.new(generator).generate end
22
22
 
23
23
  private
24
+
25
+ # %(
26
+ # update TABLE tbl
27
+ # set read_acls = (
28
+ # select array_agg(role_id)
29
+ # from acl_portal.eff_object_roles eor
30
+ # where tbl.DOMAIN_ID = eor.object_id
31
+ # )
32
+ # )
33
+
34
+ # %(
35
+ # select
36
+ # from
37
+ # where
38
+ # domain_id_of(DOMAIN, TABLE_NAME, id)
39
+ # )
40
+
24
41
  def generate_acl_update_function
25
42
  signature = "#{acl_schema}.update_acls()"
26
43
 
27
- stmts = spec.tables.map { |table|
28
- "perform #{acl_schema}.#{table}_update_acls(id) from #{app_schema}.#{table};"
44
+ stmts = spec.tables.map { |table|
45
+ "perform #{acl_schema}.#{table}_update_acls(id) from #{app_schema}.#{table};"
29
46
  }
30
47
 
31
48
  puts %(
@@ -56,7 +73,7 @@ module MkAcl
56
73
  -- Note: The different table-level variations of this function can be
57
74
  -- collapsed into a single function but it requires dynamic execution of
58
75
  -- a delete statement with array-of-array arguments :-O
59
- --
76
+ --
60
77
  -- Note: This function is auto-generated by #{$PROGRAM_NAME} using #{spec.file}
61
78
  --
62
79
  drop function if exists #{signature} cascade;
@@ -68,7 +85,7 @@ module MkAcl
68
85
  _domain_id integer;
69
86
  _acls integer[]; -- per-rule ACLs
70
87
  _acl_select integer[][]; -- per-action ACLs
71
- _acl_update integer[][]; --
88
+ _acl_update integer[][]; --
72
89
  _acl_delete integer[][]; --
73
90
  begin
74
91
  ).align
@@ -132,7 +149,7 @@ module MkAcl
132
149
  end
133
150
  puts %(
134
151
  -- Update ACL fields
135
- update #{table.uid}
152
+ update #{table.uid}
136
153
  set acl_select = _acl_select,
137
154
  acl_update = _acl_update,
138
155
  acl_delete = _acl_delete
@@ -165,7 +182,7 @@ module MkAcl
165
182
 
166
183
  -- Create attach ACLs
167
184
  insert into acl_portal.attach_acls (parent_table, parent_id, child_table, child_field, acls)
168
- with
185
+ with
169
186
  case_role_acls as (
170
187
  select
171
188
  id
@@ -182,8 +199,8 @@ module MkAcl
182
199
  where rolename = any(array[#{auth_role_list}]::text[])
183
200
  ),
184
201
  role_acls as (
185
- select * from case_role_acls
186
- union
202
+ select * from case_role_acls
203
+ union
187
204
  select * from auth_role_acls
188
205
  )
189
206
  select
@@ -192,7 +209,7 @@ module MkAcl
192
209
  l.table_name as "child_table",
193
210
  l.column_name as "child_field",
194
211
  array_agg(ra.id) as "acls"
195
- from
212
+ from
196
213
  acl.links l,
197
214
  role_acls ra
198
215
  where l.table_name = '#{table}'
@@ -17,6 +17,42 @@ module MkAcl
17
17
  @chains = {}
18
18
  end
19
19
 
20
+ # Generate a set of per-table functions that returns the associated
21
+ # case_id/event_id/vist_id for the given record. The Functions are
22
+ # generated for each table in the spec file (todo read from meta)
23
+ #
24
+ # app_portal functions:
25
+ #
26
+ # case_id_of_RECORD(object_id integer)
27
+ # event_id_of_RECORD(object_id integer)
28
+ # visit_id_of_RECORD(object_id integer)
29
+ #
30
+ # 'RECORD' is substituted with the record name of a table. Record names
31
+ # are the singular name of a table. TODO: Make it plural again
32
+ #
33
+ # acl_portal functions with a record argument (used in triggers):
34
+ #
35
+ # case_id_of_RECORD(r record)
36
+ # event_id_of_RECORD(r record)
37
+ # visit_id_of_RECORD(r record)
38
+ #
39
+ # Returns the case, event, or visit id associated with the given record.
40
+ # The record should be an ACL table record type. These functions are used
41
+ # in before insert triggers because it takes a record (eg. NEW) instead
42
+ # of an ID - this saves a lookup
43
+ #
44
+ # acl_portal general id-of functions:
45
+ #
46
+ # case_id_of(table varchar, id integer)
47
+ # event_id_of(table varchar, id integer)
48
+ # visit_id_of(table varchar, id integer)
49
+ #
50
+ # These methods are practically as efficient as using the more
51
+ # specialized versions above
52
+ #
53
+ # Returns null if not found. Note that the record has to exists because
54
+ # we access it to read the foreign keys
55
+ #
20
56
  # TODO: ACL checks so that a user can't get IDs of other users' records
21
57
  def generate
22
58
  # Find chains
@@ -44,46 +80,24 @@ module MkAcl
44
80
  # # SQL sequence of spec file tables
45
81
  # def table_seq() @table_seq ||= spec.tables.map(&:uid).join(', ') end
46
82
 
47
- # Generate a set of per-table functions that returns the associated
48
- # case_id/event_id/vist_id for the given record. The Functions are
49
- # generated for each table in the spec file
50
- #
51
- # The geneated functions are
52
- #
53
- # case_id_of_RECORD(id integer)
54
- # event_id_of_RECORD(id integer)
55
- # domain_id_of_RECORD(id integer)
56
- #
57
- # and the record variants
58
- #
59
- # case_id_of_RECORD(r record)
60
- # event_id_of_RECORD(r record)
61
- # domain_id_of_RECORD(r record)
62
- #
63
- # 'RECORD' is substituted with the record name of a table. Record names
64
- # are the singular name of a table. TODO: Make it plural again
65
- #
66
- # The domain functions returns event_id if related to an event and
67
- # case_id if not. It is meant to return the most specialized domin id for
68
- # a record. TODO Is it used?
69
- #
70
- # Returns null if not found. Note that the record has to exists because
71
- # we access it to read the foreign keys
83
+ # Generate
84
+ # case_id_of_RECORD(object_id integer)
85
+ # event_id_of_RECORD(object_id integer)
86
+ # visit_id_of_RECORD(object_id integer)
72
87
  #
73
88
  def generate_per_table_id_functions
74
- # Generate functions by domain
75
- for domain in DOMAINS
89
+ for domain in DOMAINS # Generate functions by domain
76
90
 
77
91
  # Create the identity functions first. The identity functions are
78
92
  # case_id_of_case() and event_id_of_event(). This makes some stuff
79
93
  # easier later on
80
94
  table = "#{domain}s"
81
95
  id_field = "#{domain}_id"
82
- signature = "#{acl_schema}.#{id_field}_of_#{domain}(_id integer)"
96
+ signature = "#{app_schema}.#{id_field}_of_#{domain}(_object_id integer)"
83
97
  puts %(
84
98
  drop function if exists #{signature} cascade;
85
99
  create function #{signature} returns integer as $$
86
- select _id;
100
+ select _object_id;
87
101
  $$ language sql
88
102
  set search_path to ''; -- intentionally empty
89
103
  ).align
@@ -120,47 +134,43 @@ module MkAcl
120
134
  end
121
135
  end
122
136
 
123
- # Tables that references visits, events, and cases
124
- visit_tables = chains['visits'].map(&:first)
125
-
126
- # Tables that references events and cases
127
- event_tables = chains['events'].map(&:first) - visit_tables
128
-
129
- # Tables that references only cases
130
- case_tables = chains['cases'].map(&:first) - event_tables
131
-
132
- # Create domain functions
133
- for domain, tables in { case: case_tables, event: event_tables, visit: visit_tables }
134
- for table_name in tables
135
- record_name = Prick::Inflector.singularize(table_name)
136
- signature = "#{acl_schema}.domain_id_of_#{record_name}(_id integer)"
137
- domain_function = "#{acl_schema}.#{domain}_id_of_#{record_name}"
138
- puts %(
139
- drop function if exists #{signature} cascade;
140
- create function #{signature} returns integer as $$
141
- select #{domain_function}(_id);
142
- $$ language sql
143
- set search_path to ''; -- intentionally empty
144
- ).align
145
- puts
146
- end
147
- end
137
+ # # Tables that references visits, events, and cases
138
+ # visit_tables = chains['visits'].map(&:first)
139
+ #
140
+ # # Tables that references events and cases
141
+ # event_tables = chains['events'].map(&:first) - visit_tables
142
+ #
143
+ # # Tables that references only cases
144
+ # case_tables = chains['cases'].map(&:first) - event_tables
145
+ #
146
+ # # Create domain functions
147
+ # for domain, tables in { case: case_tables, event: event_tables, visit: visit_tables }
148
+ # for table_name in tables
149
+ # record_name = Prick::Inflector.singularize(table_name)
150
+ # signature = "#{acl_schema}.domain_id_of_#{record_name}(_object_id integer)"
151
+ # domain_function = "#{acl_schema}.#{domain}_id_of_#{record_name}"
152
+ # puts %(
153
+ # drop function if exists #{signature} cascade;
154
+ # create function #{signature} returns integer as $$
155
+ # select #{domain_function}(_object_id);
156
+ # $$ language sql
157
+ # set search_path to ''; -- intentionally empty
158
+ # ).align
159
+ # puts
160
+ # end
161
+ # end
148
162
  end
149
163
 
150
- # Generate general case/event id-of functions:
151
- #
164
+ # Generate
152
165
  # case_id_of(table varchar, id integer)
153
166
  # event_id_of(table varchar, id integer)
154
- # domain_id_of(table varchar, id integer)
155
- #
156
- # These methods are practically as efficient as using the more
157
- # specialized versions above
167
+ # visit_id_of(table varchar, id integer)
158
168
  #
159
169
  def generate_general_id_functions
160
170
  for domain in DOMAINS
161
171
  domain_table = Prick::Inflector.pluralize(domain)
162
172
  field = "#{domain}_id"
163
- signature = "#{acl_schema}.#{field}_of(_table varchar, _id integer)"
173
+ signature = "#{acl_schema}.#{field}_of(_table varchar, _object_id integer)"
164
174
 
165
175
  puts %(
166
176
  drop function if exists #{signature} cascade;
@@ -173,11 +183,11 @@ module MkAcl
173
183
  for table_name in chains[domain_table].map(&:first)
174
184
  record_name = Prick::Inflector.singularize(table_name)
175
185
  function_name = "#{field}_of_#{record_name}"
176
- puts "when '#{table_name}' then #{acl_schema}.#{function_name}(_id)"
186
+ puts "when '#{table_name}' then #{acl_schema}.#{function_name}(_object_id)"
177
187
  end
178
- puts "when 'cases' then _id"
179
- puts "when 'events' then _id"
180
- puts "when 'visits' then _id"
188
+ puts "when 'cases' then _object_id"
189
+ puts "when 'events' then _object_id"
190
+ puts "when 'visits' then _object_id"
181
191
  puts "else null"
182
192
  }
183
193
  puts "end case;"
@@ -185,34 +195,13 @@ module MkAcl
185
195
  puts "$$ language sql;"
186
196
  puts
187
197
  end
188
-
189
- signature = "#{acl_schema}.domain_id_of(_table varchar, _id integer)"
190
- puts %(
191
- -- FIXME: Ugly implementation
192
- drop function if exists #{signature} cascade;
193
- create function #{signature} returns integer as $$
194
- select coalesce(
195
- acl_portal.visit_id_of(_table, _id),
196
- acl_portal.event_id_of(_table, _id),
197
- acl_portal.case_id_of(_table, _id)
198
- );
199
- $$ language sql;
200
- ).align
201
- puts
202
198
  end
203
199
 
204
- # General domain-id-of-record functions
205
- #
200
+ # Generate
206
201
  # case_id_of(r record)
207
202
  # event_id_of(r record)
208
203
  # domain_id_of(r record)
209
204
  #
210
- # Returns the case, event, or visit id associated with the given record.
211
- # The record should be an ACL table record type
212
- #
213
- # Very useful in before insert triggers because it takes a record (eg.
214
- # NEW) instead of an ID
215
- #
216
205
  def generate_general_id_of_record_functions
217
206
  links = (conn.structs %(
218
207
  select
@@ -276,22 +265,22 @@ module MkAcl
276
265
  puts
277
266
  end
278
267
 
279
- signature = "#{acl_schema}.domain_id_of(_r record)"
280
- puts %(
281
- -- Note: Ugly implementation but maybe close to optimal
282
- drop function if exists #{signature} cascade;
283
- create function #{signature} returns integer as $$
284
- declare
285
- _id integer;
286
- begin
287
- select coalesce(acl_portal.visit_id_of(_r), acl_portal.event_id_of(_r), acl_portal.case_id_of(_r))
288
- into _id;
289
- return _id;
290
- end;
291
- $$ language plpgsql
292
- set search_path to ''; -- intentionally ''
293
- ).align
294
- puts
268
+ # signature = "#{acl_schema}.domain_id_of(_r record)"
269
+ # puts %(
270
+ # -- Note: Ugly implementation but maybe close to optimal
271
+ # drop function if exists #{signature} cascade;
272
+ # create function #{signature} returns integer as $$
273
+ # declare
274
+ # _object_id integer;
275
+ # begin
276
+ # select coalesce(acl_portal.visit_id_of(_r), acl_portal.event_id_of(_r), acl_portal.case_id_of(_r))
277
+ # into _object_id;
278
+ # return _object_id;
279
+ # end;
280
+ # $$ language plpgsql
281
+ # set search_path to ''; -- intentionally ''
282
+ # ).align
283
+ # puts
295
284
  end
296
285
  end
297
286
  end