composite_primary_keys 12.0.0 → 12.0.5

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +44 -3
  3. data/README.rdoc +3 -2
  4. data/lib/composite_primary_keys.rb +57 -57
  5. data/lib/composite_primary_keys/active_model/attribute_assignment.rb +19 -0
  6. data/lib/composite_primary_keys/arel/sqlserver.rb +1 -3
  7. data/lib/composite_primary_keys/associations/through_association.rb +2 -1
  8. data/lib/composite_primary_keys/attribute_methods.rb +2 -2
  9. data/lib/composite_primary_keys/attribute_methods/primary_key.rb +13 -0
  10. data/lib/composite_primary_keys/base.rb +12 -1
  11. data/lib/composite_primary_keys/composite_arrays.rb +49 -14
  12. data/lib/composite_primary_keys/connection_adapters/abstract/database_statements.rb +8 -3
  13. data/lib/composite_primary_keys/connection_adapters/abstract_adapter.rb +1 -1
  14. data/lib/composite_primary_keys/connection_adapters/mysql/database_statements.rb +24 -0
  15. data/lib/composite_primary_keys/connection_adapters/sqlserver/database_statements.rb +33 -12
  16. data/lib/composite_primary_keys/core.rb +1 -1
  17. data/lib/composite_primary_keys/persistence.rb +2 -2
  18. data/lib/composite_primary_keys/relation.rb +100 -25
  19. data/lib/composite_primary_keys/relation/finder_methods.rb +6 -6
  20. data/lib/composite_primary_keys/relation/predicate_builder/association_query_value.rb +1 -1
  21. data/lib/composite_primary_keys/validations/uniqueness.rb +1 -1
  22. data/lib/composite_primary_keys/version.rb +1 -1
  23. data/test/abstract_unit.rb +3 -5
  24. data/test/fixtures/article.rb +4 -0
  25. data/test/fixtures/articles.yml +4 -3
  26. data/test/fixtures/comment.rb +1 -3
  27. data/test/fixtures/comments.yml +10 -9
  28. data/test/fixtures/db_definitions/db2-create-tables.sql +0 -14
  29. data/test/fixtures/db_definitions/db2-drop-tables.sql +1 -3
  30. data/test/fixtures/db_definitions/mysql.sql +7 -44
  31. data/test/fixtures/db_definitions/oracle.drop.sql +3 -9
  32. data/test/fixtures/db_definitions/oracle.sql +12 -48
  33. data/test/fixtures/db_definitions/postgresql.sql +7 -44
  34. data/test/fixtures/db_definitions/sqlite.sql +6 -42
  35. data/test/fixtures/db_definitions/sqlserver.sql +5 -41
  36. data/test/fixtures/department.rb +8 -3
  37. data/test/fixtures/departments.yml +4 -4
  38. data/test/fixtures/readings.yml +2 -2
  39. data/test/fixtures/restaurants_suburbs.yml +1 -1
  40. data/test/fixtures/streets.yml +2 -2
  41. data/test/fixtures/suburbs.yml +2 -2
  42. data/test/fixtures/user.rb +3 -2
  43. data/test/test_associations.rb +30 -23
  44. data/test/test_composite_arrays.rb +14 -0
  45. data/test/test_create.rb +15 -18
  46. data/test/test_delete.rb +3 -3
  47. data/test/test_exists.rb +5 -5
  48. data/test/test_find.rb +22 -2
  49. data/test/test_habtm.rb +2 -2
  50. data/test/test_ids.rb +5 -6
  51. data/test/test_nested_attributes.rb +0 -57
  52. data/test/test_polymorphic.rb +29 -13
  53. data/test/test_preload.rb +4 -3
  54. data/test/test_serialize.rb +2 -2
  55. data/test/test_update.rb +19 -1
  56. metadata +6 -64
  57. data/test/fixtures/hack.rb +0 -5
  58. data/test/fixtures/hacks.yml +0 -3
  59. data/test/fixtures/pk_called_id.rb +0 -5
  60. data/test/fixtures/pk_called_ids.yml +0 -11
  61. data/test/fixtures/reference_code_using_composite_key_alias.rb +0 -8
  62. data/test/fixtures/reference_code_using_simple_key_alias.rb +0 -8
  63. data/test/fixtures/seat.rb +0 -5
  64. data/test/fixtures/seats.yml +0 -9
  65. data/test/fixtures/topic.rb +0 -6
  66. data/test/fixtures/topic_source.rb +0 -7
  67. data/test/mkmf.log +0 -592
  68. data/test/test_aliases.rb +0 -18
  69. data/test/test_enum.rb +0 -21
  70. data/test/test_suite.rb +0 -35
@@ -2,20 +2,41 @@ module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module SQLServer
4
4
  module DatabaseStatements
5
- def sql_for_insert(sql, pk, id_value, sequence_name, binds)
6
- sql = if pk && self.class.use_output_inserted
7
- # CPK
8
- # quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
9
- # sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}"
10
- quoted_pks = [pk].flatten.map {|pk| "INSERTED.#{SQLServer::Utils.extract_identifiers(pk).quoted}"}
11
- sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT #{quoted_pks.join(", ")}"
12
- else
13
- "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
5
+ def sql_for_insert(sql, pk, binds)
6
+ if pk.nil?
7
+ table_name = query_requires_identity_insert?(sql)
8
+ pk = primary_key(table_name)
14
9
  end
15
10
 
16
- # CPK
17
- # super
18
- [sql, binds]
11
+ sql = if pk && use_output_inserted? && !database_prefix_remote_server?
12
+ # CPK
13
+ #quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
14
+ quoted_pk = Array(pk).map {|subkey| SQLServer::Utils.extract_identifiers(subkey).quoted}
15
+
16
+ table_name ||= get_table_name(sql)
17
+ exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql)
18
+ if exclude_output_inserted
19
+ id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? "bigint" : exclude_output_inserted
20
+ # CPK
21
+ # <<~SQL.squish
22
+ # DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type});
23
+ # #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"}
24
+ # SELECT CAST(#{quoted_pk.join(',')} AS #{id_sql_type}) FROM @ssaIdInsertTable
25
+ # SQL
26
+ <<~SQL.squish
27
+ DECLARE @ssaIdInsertTable table (#{quoted_pk.map {|subkey| "#{subkey} #{id_sql_type}"}.join(", ")});
28
+ #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk.join(', INSERTED.')} INTO @ssaIdInsertTable"}
29
+ SELECT #{quoted_pk.map {|subkey| "CAST(#{subkey} AS #{id_sql_type}) #{subkey}"}.join(", ")} FROM @ssaIdInsertTable
30
+ SQL
31
+ else
32
+ # CPK
33
+ # sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}"
34
+ sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk.join(', INSERTED.')}"
35
+ end
36
+ else
37
+ "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
38
+ end
39
+ super
19
40
  end
20
41
  end
21
42
  end
@@ -40,7 +40,7 @@ module ActiveRecord
40
40
 
41
41
  record = statement.execute([id], connection)&.first
42
42
  unless record
43
- raise RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
43
+ raise ::ActiveRecord::RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
44
44
  end
45
45
  record
46
46
  end
@@ -65,8 +65,8 @@ module ActiveRecord
65
65
  )
66
66
 
67
67
  # CPK
68
- if self.composite? && self.id.compact.empty?
69
- self.id = new_id
68
+ if self.composite?
69
+ self.id = self.id.zip(Array(new_id)).map {|id1, id2| id2.nil? ? id1 : id2}
70
70
  else
71
71
  self.id ||= new_id if self.class.primary_key
72
72
  end
@@ -27,15 +27,8 @@ module ActiveRecord
27
27
  stmt = Arel::UpdateManager.new
28
28
  # CPK
29
29
  if @klass.composite?
30
- stmt.table(table)
31
-
32
- arel_attributes = primary_keys.map do |key|
33
- arel_attribute(key)
34
- end.to_composite_keys
35
-
36
- subselect = subquery_for(arel_attributes, arel)
37
-
38
- stmt.wheres = [arel_attributes.in(subselect)]
30
+ stmt.table(arel_table)
31
+ cpk_subquery(stmt)
39
32
  else
40
33
  stmt.table(arel.join_sources.empty? ? table : arel.source)
41
34
  stmt.key = arel_attribute(primary_key)
@@ -75,22 +68,16 @@ module ActiveRecord
75
68
  end
76
69
 
77
70
  stmt = Arel::DeleteManager.new
78
- # CPK
79
- if @klass.composite?
80
- stmt.from(table)
81
-
82
- arel_attributes = primary_keys.map do |key|
83
- arel_attribute(key)
84
- end.to_composite_keys
85
71
 
86
- subselect = subquery_for(arel_attributes, arel)
87
-
88
- stmt.wheres = [arel_attributes.in(subselect)]
72
+ if @klass.composite?
73
+ stmt.from(arel_table)
74
+ cpk_subquery(stmt)
89
75
  else
90
76
  stmt.from(arel.join_sources.empty? ? table : arel.source)
91
77
  stmt.key = arel_attribute(primary_key)
92
78
  stmt.wheres = arel.constraints
93
79
  end
80
+
94
81
  stmt.take(arel.limit)
95
82
  stmt.offset(arel.offset)
96
83
  stmt.order(*arel.orders)
@@ -102,17 +89,105 @@ module ActiveRecord
102
89
  end
103
90
 
104
91
  # CPK
105
- def subquery_for(key, select)
106
- subselect = select.clone
107
- subselect.projections = key
92
+ def cpk_subquery(stmt)
93
+ # For update and delete statements we need a way to specify which records should
94
+ # get updated. By default, Rails creates a nested IN subquery that uses the primary
95
+ # key. Postgresql, Sqlite, MariaDb and Oracle support IN subqueries with multiple
96
+ # columns but MySQL and SqlServer do not. Instead SQL server supports EXISTS queries
97
+ # and MySQL supports obfuscated IN queries. Thus we need to check the type of
98
+ # database adapter to decide how to proceed.
99
+ if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) && connection.is_a?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
100
+ cpk_mysql_subquery(stmt)
101
+ elsif defined?(ActiveRecord::ConnectionAdapters::SQLServerAdapter) && connection.is_a?(ActiveRecord::ConnectionAdapters::SQLServerAdapter)
102
+ cpk_exists_subquery(stmt)
103
+ else
104
+ cpk_in_subquery(stmt)
105
+ end
106
+ end
107
+
108
+ # Used by postgresql, sqlite, mariadb and oracle. Example query:
109
+ #
110
+ # UPDATE reference_codes
111
+ # SET ...
112
+ # WHERE (reference_codes.reference_type_id, reference_codes.reference_code) IN
113
+ # (SELECT reference_codes.reference_type_id, reference_codes.reference_code
114
+ # FROM reference_codes)
115
+ def cpk_in_subquery(stmt)
116
+ # Setup the subquery
117
+ subquery = arel.clone
118
+ subquery.projections = primary_keys.map do |key|
119
+ arel_table[key]
120
+ end
121
+
122
+ where_fields = primary_keys.map do |key|
123
+ arel_table[key]
124
+ end
125
+ where = Arel::Nodes::Grouping.new(where_fields).in(subquery)
126
+ stmt.wheres = [where]
127
+ end
128
+
129
+ # CPK. This is an alternative to IN subqueries. It is used by sqlserver.
130
+ # Example query:
131
+ #
132
+ # UPDATE reference_codes
133
+ # SET ...
134
+ # WHERE EXISTS
135
+ # (SELECT 1
136
+ # FROM reference_codes cpk_child
137
+ # WHERE reference_codes.reference_type_id = cpk_child.reference_type_id AND
138
+ # reference_codes.reference_code = cpk_child.reference_code)
139
+ def cpk_exists_subquery(stmt)
140
+ arel_attributes = primary_keys.map do |key|
141
+ arel_attribute(key)
142
+ end.to_composite_keys
143
+
144
+ # Clone the query
145
+ subselect = arel.clone
146
+
147
+ # Alias the table - we assume just one table
148
+ aliased_table = subselect.froms.first
149
+ aliased_table.table_alias = "cpk_child"
150
+
151
+ # Project - really we could just set this to "1"
152
+ subselect.projections = arel_attributes
153
+
154
+ # Setup correlation to the outer query via where clauses
155
+ primary_keys.map do |key|
156
+ outer_attribute = arel_table[key]
157
+ inner_attribute = aliased_table[key]
158
+ where = outer_attribute.eq(inner_attribute)
159
+ subselect.where(where)
160
+ end
161
+ stmt.wheres = [Arel::Nodes::Exists.new(subselect)]
162
+ end
163
+
164
+ # CPK. This is the old way CPK created subqueries and is used by MySql.
165
+ # MySQL does not support referencing the same table that is being UPDATEd or
166
+ # DELETEd in a subquery so we obfuscate it. The ugly query looks like this:
167
+ #
168
+ # UPDATE `reference_codes`
169
+ # SET ...
170
+ # WHERE (reference_codes.reference_type_id, reference_codes.reference_code) IN
171
+ # (SELECT reference_type_id,reference_code
172
+ # FROM (SELECT DISTINCT `reference_codes`.`reference_type_id`, `reference_codes`.`reference_code`
173
+ # FROM `reference_codes`) __active_record_temp)
174
+ def cpk_mysql_subquery(stmt)
175
+ arel_attributes = primary_keys.map do |key|
176
+ arel_attribute(key)
177
+ end.to_composite_keys
178
+
179
+ subselect = arel.clone
180
+ subselect.projections = arel_attributes
108
181
 
109
182
  # Materialize subquery by adding distinct
110
183
  # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
111
- subselect.distinct unless select.limit || select.offset || select.orders.any?
184
+ subselect.distinct unless arel.limit || arel.offset || arel.orders.any?
185
+
186
+ key_name = arel_attributes.map(&:name).join(',')
112
187
 
113
- key_name = Array(key).map {|a_key| a_key.name }.join(',')
188
+ manager = Arel::SelectManager.new(subselect.as("__active_record_temp")).project(Arel.sql(key_name))
114
189
 
115
- Arel::SelectManager.new(subselect.as("__active_record_temp")).project(Arel.sql(key_name))
190
+ stmt.wheres = [Arel::Nodes::In.new(arel_attributes, manager.ast)]
116
191
  end
117
192
  end
118
193
  end
@@ -83,7 +83,7 @@ module CompositePrimaryKeys
83
83
 
84
84
  # CPK
85
85
  # expects_array = ids.first.kind_of?(Array)
86
- ids = CompositePrimaryKeys.normalize(ids)
86
+ ids = CompositePrimaryKeys.normalize(ids, @klass.primary_keys.length)
87
87
  expects_array = ids.flatten != ids.flatten(1)
88
88
  return ids.first if expects_array && ids.first.empty?
89
89
 
@@ -96,7 +96,7 @@ module CompositePrimaryKeys
96
96
  case ids.size
97
97
  when 0
98
98
  error_message = "Couldn't find #{model_name} without an ID"
99
- raise RecordNotFound.new(error_message, model_name, primary_key)
99
+ raise ::ActiveRecord::RecordNotFound.new(error_message, model_name, primary_key)
100
100
  when 1
101
101
  result = find_one(ids.first)
102
102
  expects_array ? [ result ] : result
@@ -154,10 +154,10 @@ module CompositePrimaryKeys
154
154
  # CPK
155
155
  if composite?
156
156
  ids = if ids.length == 1
157
- ids.first.split(CompositePrimaryKeys::ID_SEP).to_composite_keys
158
- else
159
- ids.to_composite_keys
160
- end
157
+ CompositePrimaryKeys::CompositeKeys.parse(ids.first)
158
+ else
159
+ ids.to_composite_keys
160
+ end
161
161
  end
162
162
 
163
163
  return find_some_ordered(ids) unless order_values.present?
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  if associated_table.association_join_foreign_key.is_a?(Array)
7
7
  if ids.is_a?(ActiveRecord::Relation)
8
8
  ids.map do |id|
9
- id.ids_hash
9
+ associated_table.association_join_foreign_key.zip(id.id).to_h
10
10
  end
11
11
  else
12
12
  [associated_table.association_join_foreign_key.zip(ids).to_h]
@@ -24,7 +24,7 @@ module ActiveRecord
24
24
  error_options = options.except(:case_sensitive, :scope, :conditions)
25
25
  error_options[:value] = value
26
26
 
27
- record.errors.add(attribute, :taken, error_options)
27
+ record.errors.add(attribute, :taken, **error_options)
28
28
  end
29
29
  end
30
30
  end
@@ -2,7 +2,7 @@ module CompositePrimaryKeys
2
2
  module VERSION
3
3
  MAJOR = 12
4
4
  MINOR = 0
5
- TINY = 0
5
+ TINY = 5
6
6
  STRING = [MAJOR, MINOR, TINY].join('.')
7
7
  end
8
8
  end
@@ -1,12 +1,10 @@
1
1
  spec_name = ENV['ADAPTER'] || 'sqlite'
2
2
  require 'bundler'
3
- Bundler.require(:default, spec_name.to_sym)
3
+ require 'minitest/autorun'
4
4
 
5
- # To make debugging easier, test within this source tree versus an installed gem
6
- $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
5
+ Bundler.setup(:default, spec_name.to_sym)
6
+ Bundler.require(:default, spec_name.to_sym)
7
7
  require 'composite_primary_keys'
8
- require 'minitest/autorun'
9
- require 'active_support/test_case'
10
8
 
11
9
  # Require the connection spec
12
10
  PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
@@ -2,5 +2,9 @@ class Article < ActiveRecord::Base
2
2
  validates :id, uniqueness: true, numericality: true, allow_nil: true, allow_blank: true, on: :create
3
3
  has_many :readings, :dependent => :delete_all
4
4
  has_many :users, :through => :readings
5
+
6
+ has_many :comments, :dependent => :delete_all
7
+ has_many :employee_commentators, :through => :comments, :source => :person, :source_type => :employee
8
+ has_many :user_commentators, :through => :comments, :source => :person, :source_type => "User"
5
9
  end
6
10
 
@@ -1,7 +1,8 @@
1
1
  first:
2
- id: 1
3
2
  name: Article One
4
3
 
5
4
  second:
6
- id: 2
7
- name: Article Two
5
+ name: Article Two
6
+
7
+ third:
8
+ name: Article Three
@@ -1,7 +1,5 @@
1
1
  class Comment < ActiveRecord::Base
2
2
  belongs_to :person, :polymorphic => true
3
- belongs_to :hack
4
-
5
- enum :shown => [ :true, :false ]
3
+ belongs_to :article
6
4
  end
7
5
 
@@ -1,16 +1,17 @@
1
- comment1:
1
+ employee_comment:
2
2
  id: 1
3
+ article: first
3
4
  person_id: 1
4
5
  person_type: Employee
5
-
6
- comment2:
6
+
7
+ user_comment_1:
7
8
  id: 2
9
+ article: second
8
10
  person_id: 1
9
11
  person_type: User
10
- hack_id: 7
11
-
12
- comment3:
12
+
13
+ user_comment_2:
13
14
  id: 3
14
- person_id: 7
15
- person_type: Hack
16
-
15
+ article: second
16
+ person_id: 2
17
+ person_type: User
@@ -1,17 +1,3 @@
1
- CREATE TABLE topics (
2
- id integer NOT NULL,
3
- name varchar(50) default NULL,
4
- feed_size integer default NULL,
5
- PRIMARY KEY (id)
6
- );
7
-
8
- CREATE TABLE topic_sources (
9
- topic_id integer NOT NULL,
10
- platform varchar(50) NOT NULL,
11
- keywords varchar(50) default NULL,
12
- PRIMARY KEY (topic_id,platform)
13
- );
14
-
15
1
  CREATE TABLE reference_types (
16
2
  reference_type_id integer NOT NULL generated by default as identity (start with 100, increment by 1, no cache),
17
3
  type_label varchar(50) default NULL,
@@ -14,6 +14,4 @@ drop table PRODUCT_TARIFFS;
14
14
  drop table KITCHEN_SINK;
15
15
  drop table RESTAURANTS;
16
16
  drop table RESTAURANTS_SUBURBS;
17
- drop table PRODUCTS_RESTAURANTS;
18
- drop table TOPICS;
19
- drop table TOPIC_SOURCES;
17
+ drop table PRODUCTS_RESTAURANTS;
@@ -1,17 +1,3 @@
1
- create table topics (
2
- id int not null auto_increment,
3
- name varchar(50) default null,
4
- feed_size int default null,
5
- primary key (id)
6
- );
7
-
8
- create table topic_sources (
9
- topic_id int not null,
10
- platform varchar(50) not null,
11
- keywords varchar(50) default null,
12
- primary key (topic_id,platform)
13
- );
14
-
15
1
  create table reference_types (
16
2
  reference_type_id int not null auto_increment,
17
3
  type_label varchar(50) default null,
@@ -52,7 +38,7 @@ create table product_tariffs (
52
38
  );
53
39
 
54
40
  create table suburbs (
55
- city_id int not null,
41
+ city_id int not null auto_increment,
56
42
  suburb_id int not null,
57
43
  name varchar(50) not null,
58
44
  primary key (city_id, suburb_id)
@@ -86,7 +72,7 @@ create table readings (
86
72
  primary key (id)
87
73
  );
88
74
 
89
- create table groups (
75
+ create table `groups` (
90
76
  id int not null auto_increment,
91
77
  name varchar(50) not null,
92
78
  primary key (id)
@@ -107,9 +93,9 @@ create table membership_statuses (
107
93
  );
108
94
 
109
95
  create table departments (
110
- department_id int not null,
96
+ id int not null auto_increment,
111
97
  location_id int not null,
112
- primary key (department_id, location_id)
98
+ primary key (id, location_id)
113
99
  );
114
100
 
115
101
  create table employees (
@@ -122,16 +108,9 @@ create table employees (
122
108
 
123
109
  create table comments (
124
110
  id int not null auto_increment,
125
- person_id int default null,
126
- shown int default null,
127
- person_type varchar(100) default null,
128
- hack_id int default null,
129
- primary key (id)
130
- );
131
-
132
- create table hacks (
133
- id int not null auto_increment,
134
- name varchar(50) not null,
111
+ article_id int not null,
112
+ person_id int not null,
113
+ person_type varchar(100) not null,
135
114
  primary key (id)
136
115
  );
137
116
 
@@ -184,13 +163,6 @@ create table room_assignments (
184
163
  room_id int not null
185
164
  );
186
165
 
187
- create table seats (
188
- flight_number int not null,
189
- seat int not null,
190
- customer int,
191
- primary key (flight_number, seat)
192
- );
193
-
194
166
  create table capitols (
195
167
  country varchar(100) not null,
196
168
  city varchar(100) not null,
@@ -206,13 +178,4 @@ create table products_restaurants (
206
178
  create table employees_groups (
207
179
  employee_id int not null,
208
180
  group_id int not null
209
- );
210
-
211
- create table pk_called_ids (
212
- id integer not null,
213
- reference_code int not null,
214
- code_label varchar(50) default null,
215
- abbreviation varchar(50) default null,
216
- description varchar(50) default null,
217
- primary key (id, reference_code)
218
181
  );