brick 1.0.71 → 1.0.72

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: 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