mikras_utils 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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