brick 1.0.71 → 1.0.72

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: e50dce8dd2821ec196836393569138d4e18c53a9ef1e3e910ddffa6bc3788824
4
- data.tar.gz: 982b26f7ed3a14f69589b6c422b37a5ec42b210c095cf85ef0eeab151ea80c59
3
+ metadata.gz: 06c3efed47ee7086f01a4f59c177f362343f376b1d6534f86e61ee38001b0d7b
4
+ data.tar.gz: 8b521c56eb5afc09b06aec0adf0085136cb9e94d2c1a93355a8a2fc79da0316b
5
5
  SHA512:
6
- metadata.gz: 2c0e76b3e092d35f57045f3dd106ae9b8c90b266df0a7c0c973824ed20c1ac41560f124420d164e4e9d170b9a386345f424f19bce01bf041c9ebb553c842e553
7
- data.tar.gz: 6d257bbec4c364456be5167b55c2eb377d94cdd0485e56dfb699daf14f3dce7a46c94c91534ecbe9c8f090b564e8752de0c3b88cf7560e01ca9d8388a3ec9930
6
+ metadata.gz: 1566ddaa89e45496b752b64ec218456e05dca50cc81a722d5a88e4d4bbe19b28ca981eeb253688f44e55a8ef9949d195bdc5a61f5ba7980b2507c315277b4d4d
7
+ data.tar.gz: 7643f78bf92721c89bdb0065f112592cba26023ea7745ba34551ce39d9378cd286f50b3ea1491c813e10f6d93521ef16c54fa70edc5dbcd4d70cb5ad6a859ad3
@@ -68,13 +68,14 @@ module ActiveRecord
68
68
  end
69
69
 
70
70
  def self._brick_primary_key(relation = nil)
71
- return instance_variable_get(:@_brick_primary_key) if instance_variable_defined?(:@_brick_primary_key)
71
+ return @_brick_primary_key if instance_variable_defined?(:@_brick_primary_key)
72
72
 
73
73
  pk = begin
74
74
  primary_key.is_a?(String) ? [primary_key] : primary_key || []
75
75
  rescue
76
76
  []
77
77
  end
78
+ pk.map! { |pk_part| pk_part =~ /^[A-Z0-9_]+$/ ? pk_part.downcase : pk_part } unless connection.adapter_name == 'MySQL2'
78
79
  # Just return [] if we're missing any part of the primary key. (PK is usually just "id")
79
80
  if relation && pk.present?
80
81
  @_brick_primary_key ||= pk.any? { |pk_part| !relation[:cols].key?(pk_part) } ? [] : pk
@@ -146,11 +147,8 @@ module ActiveRecord
146
147
  dsl2 << ch
147
148
  end
148
149
  end
149
- # Rewrite the DSL just in case it's different because we had to expand it
150
- unless emit_dsl
151
- # puts "Compare:\n #{dsl}\n #{dsl2}"
152
- ::Brick.config.model_descrips[name] = dsl2
153
- end
150
+ # Rewrite the DSL in case it's now different from having to expand it
151
+ ::Brick.config.model_descrips[name] = dsl2 unless emit_dsl
154
152
  else # With no DSL available, still put this prefix into the JoinArray so we can get primary key (ID) info from this table
155
153
  x = prefix.each_with_object(build_array) { |v, s| s[v.to_sym] }
156
154
  x[prefix.last] = nil unless prefix.empty? # Using []= will "hydrate" any missing part(s) in our whole series
@@ -323,7 +321,7 @@ module ActiveRecord
323
321
  end
324
322
 
325
323
  class Relation
326
- attr_reader :_brick_chains
324
+ attr_reader :_brick_chains, :_arel_applied_aliases
327
325
 
328
326
  # CLASS STUFF
329
327
  def _recurse_arel(piece, prefix = '')
@@ -361,7 +359,7 @@ module ActiveRecord
361
359
  # parent = piece.left == on.left.relation ? on.right.relation : on.left.relation
362
360
  # binding.pry if piece.left.is_a?(Arel::Nodes::TableAlias)
363
361
  if table.is_a?(Arel::Nodes::TableAlias)
364
- alias_name = table.right
362
+ @_arel_applied_aliases << (alias_name = table.right)
365
363
  table = table.left
366
364
  end
367
365
  (_brick_chains[table._arel_table_type] ||= []) << (alias_name || table.table_alias || table.name)
@@ -387,6 +385,7 @@ module ActiveRecord
387
385
 
388
386
  # INSTANCE STUFF
389
387
  def _arel_alias_names
388
+ @_arel_applied_aliases = []
390
389
  # %%% If with Rails 3.1 and older you get "NoMethodError: undefined method `eq' for nil:NilClass"
391
390
  # when trying to call relation.arel, then somewhere along the line while navigating a has_many
392
391
  # relationship it can't find the proper foreign key.
@@ -429,22 +428,7 @@ module ActiveRecord
429
428
  if selects&.empty? # Default to all columns
430
429
  tbl_no_schema = table.name.split('.').last
431
430
  columns.each do |col|
432
- if (col_name = col.name) == 'class'
433
- col_alias = " AS #{col.name}_"
434
- else
435
- alias_name = nil
436
- idx = 0
437
- col_name.each_char do |c|
438
- unless (c >= 'a' && c <= 'z') ||
439
- c == '_' ||
440
- (c >= 'A' && c <= 'Z') ||
441
- (c >= '0' && c <= '9')
442
- (alias_name ||= col_name.dup)[idx] = 'x'
443
- end
444
- ++idx
445
- end
446
- col_alias = " AS #{alias_name}" if alias_name
447
- end
431
+ col_alias = " AS #{col.name}_" if (col_name = col.name) == 'class'
448
432
  selects << if is_mysql
449
433
  "`#{tbl_no_schema}`.`#{col_name}`#{col_alias}"
450
434
  elsif is_postgres || is_mssql
@@ -452,8 +436,8 @@ module ActiveRecord
452
436
  cast_as_text = '::text' if is_distinct && Brick.relations[klass.table_name]&.[](:cols)&.[](col_name)&.first&.start_with?('xml')
453
437
  "\"#{tbl_no_schema}\".\"#{col_name}\"#{cast_as_text}#{col_alias}"
454
438
  elsif col.type # Could be Sqlite or Oracle
455
- if col_alias
456
- "#{tbl_no_schema}.#{col_name}#{col_alias}"
439
+ if col_alias || !(/^[a-z0-9_]+$/ =~ col_name)
440
+ "#{tbl_no_schema}.\"#{col_name}\"#{col_alias}"
457
441
  else
458
442
  "#{tbl_no_schema}.#{col_name}"
459
443
  end
@@ -478,9 +462,14 @@ module ActiveRecord
478
462
  next if chains[k1].nil?
479
463
 
480
464
  tbl_name = (field_tbl_names[v.first][k1] ||= shift_or_first(chains[k1])).split('.').last
465
+ # If it's Oracle, quote any AREL aliases that had been applied
466
+ tbl_name = "\"#{tbl_name}\"" if ::Brick.is_oracle && rel_dupe._arel_applied_aliases.include?(tbl_name)
481
467
  field_tbl_name = nil
482
468
  v1.map { |x| [translations[x[0..-2].map(&:to_s).join('.')], x.last] }.each_with_index do |sel_col, idx|
469
+ # binding.pry if chains[sel_col.first].nil?
483
470
  field_tbl_name = (field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])).split('.').last
471
+ # If it's Oracle, quote any AREL aliases that had been applied
472
+ field_tbl_name = "\"#{field_tbl_name}\"" if ::Brick.is_oracle && rel_dupe._arel_applied_aliases.include?(field_tbl_name)
484
473
 
485
474
  # Postgres can not use DISTINCT with any columns that are XML, so for any of those just convert to text
486
475
  is_xml = is_distinct && Brick.relations[sel_col.first.table_name]&.[](:cols)&.[](sel_col.last)&.first&.start_with?('xml')
@@ -751,9 +740,11 @@ Module.class_exec do
751
740
  base_module == Object && # %%% This works for Person::Person -- but also limits us to not being able to allow more than one level of namespacing
752
741
  (schema_name = [(singular_table_name = class_name.underscore),
753
742
  (table_name = singular_table_name.pluralize),
754
- class_name,
743
+ ::Brick.is_oracle ? class_name.upcase : class_name,
755
744
  (plural_class_name = class_name.pluralize)].find { |s| Brick.db_schemas.include?(s) }&.camelize ||
756
745
  (::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}::") && class_name))
746
+ return self.const_get(schema_name) if self.const_defined?(schema_name)
747
+
757
748
  # Build out a module for the schema if it's namespaced
758
749
  # schema_name = schema_name.camelize
759
750
  base_module.const_set(schema_name.to_sym, (built_module = Module.new))
@@ -1386,8 +1377,13 @@ class Object
1386
1377
  [new_alt_name, true]
1387
1378
  else
1388
1379
  assoc_name = ::Brick.namify(hm_assoc[:inverse_table]).pluralize
1380
+ if (needs_class = assoc_name.include?('.')) # If there is a schema name present, use a downcased version for the :has_many
1381
+ assoc_parts = assoc_name.split('.')
1382
+ assoc_parts[0].downcase! if assoc_parts[0] =~ /^[A-Z0-9_]+$/
1383
+ assoc_name = assoc_parts.join('.')
1384
+ end
1389
1385
  # hm_assoc[:assoc_name] = assoc_name
1390
- [assoc_name, assoc_name.include?('.')]
1386
+ [assoc_name, needs_class]
1391
1387
  end
1392
1388
  end
1393
1389
  end
@@ -1443,7 +1439,8 @@ module ActiveRecord::ConnectionHandling
1443
1439
  row['table_schema']
1444
1440
  end
1445
1441
  # Remove any system schemas
1446
- s[row] = nil unless ['information_schema', 'pg_catalog'].include?(row)
1442
+ s[row] = nil unless ['information_schema', 'pg_catalog',
1443
+ 'INFORMATION_SCHEMA', 'sys'].include?(row)
1447
1444
  end
1448
1445
  if (is_multitenant = (multitenancy = ::Brick.config.schema_behavior[:multitenant]) &&
1449
1446
  (sta = multitenancy[:schema_to_analyse]) != 'public') &&
@@ -1487,6 +1484,7 @@ module ActiveRecord::ConnectionHandling
1487
1484
  # ActiveRecord::Base.internal_metadata_table_name, ActiveRecord::Base.schema_migrations_table_name
1488
1485
  # For if it's not SQLite -- so this is the Postgres and MySQL version
1489
1486
  measures = []
1487
+ ::Brick.is_oracle = true if ActiveRecord::Base.connection.adapter_name == 'OracleEnhanced'
1490
1488
  case ActiveRecord::Base.connection.adapter_name
1491
1489
  when 'PostgreSQL', 'SQLite' # These bring back a hash for each row because the query uses column aliases
1492
1490
  # schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
@@ -1538,15 +1536,21 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
1538
1536
  else
1539
1537
  ActiveRecord::Base.retrieve_schema_and_tables(sql)
1540
1538
  end
1539
+
1541
1540
  schema_and_tables.each do |r|
1542
1541
  next if r[1].index('$') # Oracle can have goofy table names with $
1543
1542
 
1544
- if (relation_name = r[1]) =~ /^[A-Z_]+$/
1543
+ if (relation_name = r[1]) =~ /^[A-Z0-9_]+$/
1545
1544
  relation_name.downcase!
1546
1545
  end
1546
+ # Expect the default schema for SQL Server to be 'dbo'.
1547
+ if (::Brick.is_oracle && r[0] != schema) || (is_mssql && r[0] != 'dbo')
1548
+ relation_name = "#{r[0]}.#{relation_name}"
1549
+ end
1550
+
1547
1551
  relation = relations[relation_name] # here relation represents a table or view from the database
1548
1552
  relation[:isView] = true if r[2] == 'VIEW' # table_type
1549
- col_name = r[3]
1553
+ col_name = ::Brick.is_oracle ? connection.send(:oracle_downcase, r[3]) : r[3]
1550
1554
  key = case r[6] # constraint type
1551
1555
  when 'PRIMARY KEY'
1552
1556
  # key
@@ -1558,8 +1562,8 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
1558
1562
  end
1559
1563
  key << col_name if key
1560
1564
  cols = relation[:cols] # relation.fetch(:cols) { relation[:cols] = [] }
1561
- # 'data_type', 'max_length'
1562
- cols[col_name] = [r[4], r[5], measures&.include?(col_name)]
1565
+ # 'data_type', 'max_length', measure, 'is_nullable'
1566
+ cols[col_name] = [r[4], r[5], measures&.include?(col_name), r[8] == 'NO']
1563
1567
  # puts "KEY! #{r['relation_name']}.#{col_name} #{r['key']} #{r['const']}" if r['key']
1564
1568
  end
1565
1569
  end
@@ -1612,10 +1616,10 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
1612
1616
  schemas = ::Brick.db_schemas.keys.map { |s| "'#{s}'" }.join(', ')
1613
1617
  sql =
1614
1618
  "SELECT -- fk
1615
- ac.owner AS constraint_schema, acc_fk.table_name, LOWER(acc_fk.column_name),
1619
+ ac.owner AS constraint_schema, acc_fk.table_name, acc_fk.column_name,
1616
1620
  -- referenced pk
1617
1621
  ac.r_owner AS primary_schema, acc_pk.table_name AS primary_table, acc_fk.constraint_name AS constraint_schema_fk
1618
- -- , LOWER(acc_pk.column_name)
1622
+ -- , acc_pk.column_name
1619
1623
  FROM all_cons_columns acc_fk
1620
1624
  INNER JOIN all_constraints ac ON acc_fk.owner = ac.owner
1621
1625
  AND acc_fk.constraint_name = ac.constraint_name
@@ -1633,49 +1637,71 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
1633
1637
  # Multitenancy makes things a little more general overall, except for non-tenanted tables
1634
1638
  if apartment_excluded&.include?(::Brick.namify(fk[1]).singularize.camelize)
1635
1639
  fk[0] = Apartment.default_schema
1636
- elsif is_postgres && (fk[0] == 'public' || (is_multitenant && fk[0] == schema)) ||
1637
- !is_postgres && ['mysql', 'performance_schema', 'sys'].exclude?(fk[0])
1640
+ elsif (is_postgres && (fk[0] == 'public' || (is_multitenant && fk[0] == schema))) ||
1641
+ (::Brick.is_oracle && fk[0] == schema) ||
1642
+ (is_mssql && fk[0] == 'dbo') ||
1643
+ (!is_postgres && !::Brick.is_oracle && !is_mssql && ['mysql', 'performance_schema', 'sys'].exclude?(fk[0]))
1638
1644
  fk[0] = nil
1639
1645
  end
1640
1646
  if apartment_excluded&.include?(fk[4].singularize.camelize)
1641
1647
  fk[3] = Apartment.default_schema
1642
- elsif is_postgres && (fk[3] == 'public' || (is_multitenant && fk[3] == schema)) ||
1643
- !is_postgres && ['mysql', 'performance_schema', 'sys'].exclude?(fk[3])
1648
+ elsif (is_postgres && (fk[3] == 'public' || (is_multitenant && fk[3] == schema))) ||
1649
+ (::Brick.is_oracle && fk[3] == schema) ||
1650
+ (is_mssql && fk[3] == 'dbo') ||
1651
+ (!is_postgres && !::Brick.is_oracle && !is_mssql && ['mysql', 'performance_schema', 'sys'].exclude?(fk[3]))
1644
1652
  fk[3] = nil
1645
1653
  end
1646
- fk[1].downcase! if ::Brick.is_oracle && fk[1] =~ /^[A-Z_]+$/
1647
- fk[4].downcase! if ::Brick.is_oracle && fk[4] =~ /^[A-Z_]+$/
1654
+ if ::Brick.is_oracle
1655
+ fk[1].downcase! if fk[1] =~ /^[A-Z0-9_]+$/
1656
+ fk[4].downcase! if fk[4] =~ /^[A-Z0-9_]+$/
1657
+ fk[2] = connection.send(:oracle_downcase, fk[2])
1658
+ end
1648
1659
  ::Brick._add_bt_and_hm(fk, relations)
1649
1660
  end
1650
1661
  end
1651
1662
 
1652
1663
  tables = []
1653
1664
  views = []
1665
+ table_class_length = 0
1666
+ view_class_length = 0
1654
1667
  relations.each do |k, v|
1655
1668
  name_parts = k.split('.')
1656
1669
  idx = 1
1657
1670
  name_parts = name_parts.map do |x|
1658
- ((idx += 1) < name_parts.length ? x.singularize : x).camelize
1671
+ (idx += 1) < name_parts.length ? x : x.singularize
1659
1672
  end
1673
+ name_parts.shift if apartment && name_parts.length > 1 && name_parts.first == Apartment.default_schema
1674
+ class_name = name_parts.map(&:camelize).join('::')
1660
1675
  if v.key?(:isView)
1676
+ view_class_length = class_name.length if class_name.length > view_class_length
1661
1677
  views
1662
1678
  else
1663
- name_parts.shift if apartment && name_parts.length > 1 && name_parts.first == Apartment.default_schema
1679
+ table_class_length = class_name.length if class_name.length > table_class_length
1664
1680
  tables
1665
- end << name_parts.join('::')
1681
+ end << [class_name, name_parts]
1666
1682
  end
1667
- unless tables.empty?
1668
- puts "\nClasses that can be built from tables:"
1669
- tables.sort.each { |x| puts x }
1683
+ puts "\n" if tables.present? || views.present?
1684
+ if tables.present?
1685
+ puts "Classes that can be built from tables:"
1686
+ display_classes(tables, table_class_length)
1670
1687
  end
1671
- unless views.empty?
1672
- puts "\nClasses that can be built from views:"
1673
- views.sort.each { |x| puts x }
1688
+ if views.present?
1689
+ puts "Classes that can be built from views:"
1690
+ display_classes(views, view_class_length)
1674
1691
  end
1675
1692
 
1676
1693
  ::Brick.load_additional_references if initializer_loaded
1677
1694
  end
1678
1695
 
1696
+ def display_classes(rels, max_length)
1697
+ rels.sort.each do |rel|
1698
+ rel_link = rel.last.dup.map(&:underscore)
1699
+ rel_link[-1] = rel_link[-1].pluralize
1700
+ puts "#{rel.first}#{' ' * (max_length - rel.first.length)} /#{rel_link.join('/')}"
1701
+ end
1702
+ puts "\n"
1703
+ end
1704
+
1679
1705
  def retrieve_schema_and_tables(sql = nil, is_postgres = nil, is_mssql = nil, schema = nil)
1680
1706
  is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer' if is_mssql.nil?
1681
1707
  sql ||= "SELECT t.table_schema AS \"schema\", t.table_name AS relation_name, t.table_type,#{"
@@ -1705,7 +1731,8 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
1705
1731
  -- AND kcu.position_in_unique_constraint IS NULL" unless is_mssql}
1706
1732
  AND kcu.ordinal_position = c.ordinal_position
1707
1733
  WHERE t.table_schema #{is_postgres || is_mssql ?
1708
- "NOT IN ('information_schema', 'pg_catalog')"
1734
+ "NOT IN ('information_schema', 'pg_catalog',
1735
+ 'INFORMATION_SCHEMA', 'sys')"
1709
1736
  :
1710
1737
  "= '#{ActiveRecord::Base.connection.current_database.tr("'", "''")}'"}#{"
1711
1738
  AND t.table_schema = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema }
@@ -1750,7 +1777,7 @@ module Brick
1750
1777
 
1751
1778
  class << self
1752
1779
  def _add_bt_and_hm(fk, relations, is_polymorphic = false, is_optional = false)
1753
- bt_assoc_name = ::Brick.namify(fk[2])
1780
+ bt_assoc_name = ::Brick.namify(fk[2], true)
1754
1781
  unless is_polymorphic
1755
1782
  bt_assoc_name = if bt_assoc_name.underscore.end_with?('_id')
1756
1783
  bt_assoc_name[-3] == '_' ? bt_assoc_name[0..-4] : bt_assoc_name[0..-3]
@@ -87,9 +87,9 @@ module Brick
87
87
 
88
88
  def path_keys(hm_assoc, fk_name, obj_name, pk)
89
89
  keys = if fk_name.is_a?(Array) && pk.is_a?(Array) # Composite keys?
90
- fk_name.zip(pk.map { |pk_part| "#{obj_name}.#{pk_part}" })
90
+ fk_name.zip(pk.map { |fk_part| "#{obj_name}.#{fk_part}" })
91
91
  else
92
- pk = pk.each_with_object([]) { |pk_part, s| s << "#{obj_name}.#{pk_part}" }
92
+ pk = pk.map { |pk_part| "#{obj_name}.#{pk_part}" }
93
93
  [[fk_name, pk.length == 1 ? pk.first : pk.inspect]]
94
94
  end
95
95
  keys << [hm_assoc.inverse_of.foreign_type, hm_assoc.active_record.name] if hm_assoc.options.key?(:as)
@@ -106,8 +106,8 @@ module Brick
106
106
  return _brick_find_template(*args, **options)
107
107
  end
108
108
 
109
- if @_brick_model
110
- pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(model_name, nil))
109
+ if @_brick_model
110
+ pk = @_brick_model._brick_primary_key(::Brick.relations.fetch(@_brick_model&.table_name, nil))
111
111
  obj_name = model_name.split('::').last.underscore
112
112
  path_obj_name = model_name.underscore.tr('/', '_')
113
113
  table_name = obj_name.pluralize
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 71
8
+ TINY = 72
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
data/lib/brick.rb CHANGED
@@ -152,7 +152,8 @@ module Brick
152
152
  end
153
153
 
154
154
  # Convert spaces to underscores if the second character and onwards is mixed case
155
- def namify(name)
155
+ def namify(name, attempt_downcase = false)
156
+ name.downcase! if attempt_downcase && name =~ /^[A-Z0-9_]+$/
156
157
  if name.include?(' ')
157
158
  # All uppers or all lowers?
158
159
  if name[1..-1] =~ /^[A-Z0-9_]+$/ || name[1..-1] =~ /^[a-z0-9_]+$/
@@ -726,6 +727,63 @@ ActiveSupport.on_load(:active_record) do
726
727
  end
727
728
  end
728
729
  end
730
+
731
+ # Migration stuff
732
+ module ConnectionAdapters
733
+ # Override the downcasing implementation from the OracleEnhanced gem as it has bad regex
734
+ if const_defined?(:OracleEnhanced)
735
+ module OracleEnhanced::Quoting
736
+ private
737
+
738
+ def oracle_downcase(column_name)
739
+ return nil if column_name.nil?
740
+
741
+ /^[A-Za-z0-9_]+$/ =~ column_name ? column_name.downcase : column_name
742
+ end
743
+ end
744
+ end
745
+ if const_defined?(:SQLServerAdapter)
746
+ class SQLServer::TableDefinition
747
+ alias _brick_new_column_definition new_column_definition
748
+ def new_column_definition(name, type, **options)
749
+ case type
750
+ when :serial
751
+ type = :integer
752
+ options[:is_identity] = true
753
+ when :bigserial
754
+ type = :bigint
755
+ options[:is_identity] = true
756
+ end
757
+ _brick_new_column_definition(name, type, **options)
758
+ end
759
+ def serial(*args)
760
+ options = args.extract_options!
761
+ options[:is_identity] = true
762
+ args.each { |name| column(name, 'integer', options) }
763
+ end
764
+ def bigserial(*args)
765
+ options = args.extract_options!
766
+ options[:is_identity] = true
767
+ args.each { |name| column(name, 'bigint', options) }
768
+ end
769
+ # Seems that geography gets used a fair bit in MSSQL
770
+ def geography(*args)
771
+ options = args.extract_options!
772
+ # options[:precision] ||= 8
773
+ # options[:scale] ||= 2
774
+ args.each { |name| column(name, 'geography', options) }
775
+ end
776
+ end
777
+ class SQLServerAdapter
778
+ unless respond_to?(:schema_exists?)
779
+ def schema_exists?(schema)
780
+ schema_sql = 'SELECT 1 FROM sys.schemas WHERE name = ?'
781
+ ActiveRecord::Base.execute_sql(schema_sql, schema).present?
782
+ end
783
+ end
784
+ end
785
+ end
786
+ end
729
787
  end
730
788
  # rubocop:enable Lint/ConstantDefinitionInBlock
731
789
 
@@ -23,6 +23,21 @@ module Brick
23
23
  'time with time zone' => 'time',
24
24
  'double precision' => 'float',
25
25
  'smallint' => 'integer', # %%% Need to put in "limit: 2"
26
+ # # Oracle data types
27
+ 'VARCHAR2' => 'string',
28
+ 'CHAR' => 'string',
29
+ ['NUMBER', 22] => 'integer',
30
+ /^INTERVAL / => 'string', # Time interval stuff like INTERVAL YEAR(2) TO MONTH, INTERVAL '999' DAY(3), etc
31
+ 'XMLTYPE' => 'xml',
32
+ 'RAW' => 'binary',
33
+ 'SDO_GEOMETRY' => 'geometry',
34
+ # MSSQL data types
35
+ 'int' => 'integer',
36
+ 'nvarchar' => 'string',
37
+ 'nchar' => 'string',
38
+ 'datetime2' => 'timestamp',
39
+ 'bit' => 'boolean',
40
+ 'varbinary' => 'binary',
26
41
  # Sqlite data types
27
42
  'TEXT' => 'text',
28
43
  '' => 'string',
@@ -122,7 +137,7 @@ module Brick
122
137
  fringe.each do |tbl|
123
138
  next unless (relation = ::Brick.relations.fetch(tbl, nil))&.fetch(:cols, nil)&.present?
124
139
 
125
- pkey_cols = (rpk = relation[:pkey].values.flatten) & (arpk = [ActiveRecord::Base.primary_key].flatten)
140
+ pkey_cols = (rpk = relation[:pkey].values.flatten) & (arpk = [ApplicationRecord.primary_key].flatten.sort)
126
141
  # In case things aren't as standard
127
142
  if pkey_cols.empty?
128
143
  pkey_cols = if rpk.empty? && relation[:cols][arpk.first]&.first == key_type
@@ -141,7 +156,7 @@ module Brick
141
156
  end
142
157
  unless schema.blank? || built_schemas.key?(schema)
143
158
  mig = +" def change\n create_schema(:#{schema}) unless schema_exists?(:#{schema})\n end\n"
144
- migration_file_write(mig_path, "create_db_schema_#{schema}", current_mig_time += 1.minute, ar_version, mig)
159
+ migration_file_write(mig_path, "create_db_schema_#{schema.underscore}", current_mig_time += 1.minute, ar_version, mig)
145
160
  built_schemas[schema] = nil
146
161
  end
147
162
 
@@ -151,11 +166,16 @@ module Brick
151
166
  # a column definition which includes :primary_key -- %%% also using a data type of bigserial or serial
152
167
  # if this one has come in as bigint or integer.
153
168
  pk_is_also_fk = fkey_cols.any? { |assoc| pkey_cols&.first == assoc[:fk] } ? pkey_cols&.first : nil
154
- # Support missing primary key (by adding: ,id: false)
169
+ # Support missing primary key (by adding: , id: false)
155
170
  id_option = if pk_is_also_fk || !pkey_cols&.present?
171
+ needs_serial_col = true
156
172
  ', id: false'
157
- elsif ((pkey_col_first = relation[:cols][pkey_cols&.first]&.first) &&
158
- (pkey_col_first = SQL_TYPES[pkey_col_first] || pkey_col_first) != key_type)
173
+ elsif ((pkey_col_first = (col_def = relation[:cols][pkey_cols&.first])&.first) &&
174
+ (pkey_col_first = SQL_TYPES[pkey_col_first] || SQL_TYPES[col_def&.[](0..1)] ||
175
+ SQL_TYPES.find { |r| r.first.is_a?(Regexp) && pkey_col_first =~ r.first }&.last ||
176
+ pkey_col_first
177
+ ) != key_type
178
+ )
159
179
  case pkey_col_first
160
180
  when 'integer'
161
181
  ', id: :serial'
@@ -164,9 +184,14 @@ module Brick
164
184
  else
165
185
  ", id: :#{pkey_col_first}" # Something like: id: :integer, primary_key: :businessentityid
166
186
  end +
167
- (pkey_cols.first ? ", primary_key: :#{pkey_cols.first}" : '') +
168
- (!is_4x_rails && (comment = relation&.fetch(:description, nil))&.present? ? ", comment: #{comment.inspect}" : '')
187
+ (pkey_cols.first ? ", primary_key: :#{pkey_cols.first}" : '')
169
188
  end
189
+ if !id_option && pkey_cols.sort != arpk
190
+ id_option = ", primary_key: :#{pkey_cols.first}"
191
+ end
192
+ if !is_4x_rails && (comment = relation&.fetch(:description, nil))&.present?
193
+ (id_option ||= +'') << ", comment: #{comment.inspect}"
194
+ end
170
195
  # Find the ActiveRecord class in order to see if the columns have comments
171
196
  unless is_4x_rails
172
197
  klass = begin
@@ -187,18 +212,24 @@ module Brick
187
212
  possible_ts = [] # Track possible generic timestamps
188
213
  add_fks = [] # Track foreign keys to add after table creation
189
214
  relation[:cols].each do |col, col_type|
190
- sql_type = SQL_TYPES[col_type.first] || col_type.first
191
- suffix = col_type[3] ? +', null: false' : +''
215
+ sql_type = SQL_TYPES[col_type.first] || SQL_TYPES[col_type[0..1]] ||
216
+ SQL_TYPES.find { |r| r.first.is_a?(Regexp) && col_type.first =~ r.first }&.last ||
217
+ col_type.first
218
+ suffix = col_type[3] || pkey_cols&.include?(col) ? +', null: false' : +''
192
219
  if !is_4x_rails && klass && (comment = klass.columns_hash.fetch(col, nil)&.comment)&.present?
193
220
  suffix << ", comment: #{comment.inspect}"
194
221
  end
195
222
  # Determine if this column is used as part of a foreign key
196
- if fk = fkey_cols.find { |assoc| col == assoc[:fk] }
223
+ if (fk = fkey_cols.find { |assoc| col == assoc[:fk] })
197
224
  to_table = fk[:inverse_table].split('.')
198
225
  to_table = to_table.length == 1 ? ":#{to_table.first}" : "'#{fk[:inverse_table]}'"
226
+ if needs_serial_col && pkey_cols&.include?(col) && (new_serial_type = {'integer' => 'serial', 'bigint' => 'bigserial'}[sql_type])
227
+ sql_type = new_serial_type
228
+ needs_serial_col = false
229
+ end
199
230
  if fk[:fk] != "#{fk[:assoc_name].singularize}_id" # Need to do our own foreign_key tricks, not use references?
200
231
  column = fk[:fk]
201
- mig << " t.#{sql_type} :#{column}#{suffix}\n"
232
+ mig << emit_column(sql_type, column, suffix)
202
233
  add_fks << [to_table, column, ::Brick.relations[fk[:inverse_table]]]
203
234
  else
204
235
  suffix << ", type: :#{sql_type}" unless sql_type == key_type
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brick
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.71
4
+ version: 1.0.72
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-16 00:00:00.000000000 Z
11
+ date: 2022-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord