activerecord 3.1.12 → 3.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (99) hide show
  1. data/CHANGELOG.md +6263 -103
  2. data/README.rdoc +2 -2
  3. data/examples/performance.rb +55 -31
  4. data/lib/active_record.rb +28 -2
  5. data/lib/active_record/aggregations.rb +2 -2
  6. data/lib/active_record/associations.rb +82 -69
  7. data/lib/active_record/associations/association.rb +2 -37
  8. data/lib/active_record/associations/association_scope.rb +3 -30
  9. data/lib/active_record/associations/builder/association.rb +6 -4
  10. data/lib/active_record/associations/builder/belongs_to.rb +3 -3
  11. data/lib/active_record/associations/builder/collection_association.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +4 -4
  13. data/lib/active_record/associations/builder/has_one.rb +5 -6
  14. data/lib/active_record/associations/builder/singular_association.rb +3 -16
  15. data/lib/active_record/associations/collection_association.rb +55 -28
  16. data/lib/active_record/associations/collection_proxy.rb +1 -35
  17. data/lib/active_record/associations/has_many_association.rb +5 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +11 -8
  19. data/lib/active_record/associations/join_dependency.rb +1 -1
  20. data/lib/active_record/associations/preloader/association.rb +3 -1
  21. data/lib/active_record/attribute_assignment.rb +221 -0
  22. data/lib/active_record/attribute_methods.rb +212 -32
  23. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  24. data/lib/active_record/attribute_methods/dirty.rb +3 -3
  25. data/lib/active_record/attribute_methods/primary_key.rb +62 -25
  26. data/lib/active_record/attribute_methods/read.rb +69 -80
  27. data/lib/active_record/attribute_methods/serialization.rb +89 -0
  28. data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -14
  29. data/lib/active_record/attribute_methods/write.rb +27 -5
  30. data/lib/active_record/autosave_association.rb +23 -8
  31. data/lib/active_record/base.rb +223 -1712
  32. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +98 -132
  33. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +82 -29
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +13 -42
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -13
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +78 -43
  40. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
  41. data/lib/active_record/connection_adapters/mysql2_adapter.rb +138 -578
  42. data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -658
  43. data/lib/active_record/connection_adapters/postgresql_adapter.rb +144 -94
  44. data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
  45. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
  46. data/lib/active_record/connection_adapters/sqlite_adapter.rb +43 -22
  47. data/lib/active_record/counter_cache.rb +1 -1
  48. data/lib/active_record/dynamic_matchers.rb +79 -0
  49. data/lib/active_record/errors.rb +11 -1
  50. data/lib/active_record/explain.rb +83 -0
  51. data/lib/active_record/explain_subscriber.rb +21 -0
  52. data/lib/active_record/fixtures.rb +31 -76
  53. data/lib/active_record/fixtures/file.rb +65 -0
  54. data/lib/active_record/identity_map.rb +1 -7
  55. data/lib/active_record/inheritance.rb +167 -0
  56. data/lib/active_record/integration.rb +49 -0
  57. data/lib/active_record/locking/optimistic.rb +19 -11
  58. data/lib/active_record/locking/pessimistic.rb +1 -1
  59. data/lib/active_record/log_subscriber.rb +3 -3
  60. data/lib/active_record/migration.rb +38 -29
  61. data/lib/active_record/migration/command_recorder.rb +7 -7
  62. data/lib/active_record/model_schema.rb +362 -0
  63. data/lib/active_record/nested_attributes.rb +3 -2
  64. data/lib/active_record/persistence.rb +51 -1
  65. data/lib/active_record/querying.rb +58 -0
  66. data/lib/active_record/railtie.rb +24 -28
  67. data/lib/active_record/railties/controller_runtime.rb +3 -1
  68. data/lib/active_record/railties/databases.rake +133 -77
  69. data/lib/active_record/readonly_attributes.rb +26 -0
  70. data/lib/active_record/reflection.rb +7 -15
  71. data/lib/active_record/relation.rb +78 -35
  72. data/lib/active_record/relation/batches.rb +5 -2
  73. data/lib/active_record/relation/calculations.rb +27 -6
  74. data/lib/active_record/relation/delegation.rb +49 -0
  75. data/lib/active_record/relation/finder_methods.rb +5 -4
  76. data/lib/active_record/relation/predicate_builder.rb +13 -16
  77. data/lib/active_record/relation/query_methods.rb +59 -4
  78. data/lib/active_record/result.rb +1 -1
  79. data/lib/active_record/sanitization.rb +194 -0
  80. data/lib/active_record/schema_dumper.rb +5 -2
  81. data/lib/active_record/scoping.rb +152 -0
  82. data/lib/active_record/scoping/default.rb +140 -0
  83. data/lib/active_record/scoping/named.rb +202 -0
  84. data/lib/active_record/serialization.rb +1 -43
  85. data/lib/active_record/serializers/xml_serializer.rb +2 -44
  86. data/lib/active_record/session_store.rb +11 -11
  87. data/lib/active_record/store.rb +50 -0
  88. data/lib/active_record/test_case.rb +11 -7
  89. data/lib/active_record/timestamp.rb +16 -3
  90. data/lib/active_record/transactions.rb +5 -5
  91. data/lib/active_record/translation.rb +22 -0
  92. data/lib/active_record/validations.rb +1 -1
  93. data/lib/active_record/validations/associated.rb +5 -4
  94. data/lib/active_record/validations/uniqueness.rb +4 -4
  95. data/lib/active_record/version.rb +3 -3
  96. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
  97. metadata +48 -38
  98. checksums.yaml +0 -7
  99. data/lib/active_record/named_scope.rb +0 -200
@@ -1,8 +1,6 @@
1
1
  require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_support/core_ext/kernel/requires'
3
2
  require 'active_support/core_ext/object/blank'
4
3
  require 'active_record/connection_adapters/statement_pool'
5
- require 'arel/visitors/bind_visitor'
6
4
 
7
5
  # Make sure we're using pg high enough for PGResult#values
8
6
  gem 'pg', '~> 0.11'
@@ -201,7 +199,7 @@ module ActiveRecord
201
199
  # * <tt>:password</tt> - Defaults to nothing.
202
200
  # * <tt>:database</tt> - The name of the database. No default, must be provided.
203
201
  # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
204
- # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
202
+ # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
205
203
  # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
206
204
  # <encoding></tt> call on the connection.
207
205
  # * <tt>:min_messages</tt> - An optional client min messages that is used in a
@@ -249,6 +247,10 @@ module ActiveRecord
249
247
  true
250
248
  end
251
249
 
250
+ def supports_index_sort_order?
251
+ true
252
+ end
253
+
252
254
  class StatementPool < ConnectionAdapters::StatementPool
253
255
  def initialize(connection, max)
254
256
  super
@@ -301,17 +303,11 @@ module ActiveRecord
301
303
  end
302
304
  end
303
305
 
304
- class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
305
- include Arel::Visitors::BindVisitor
306
- end
307
-
308
306
  # Initializes and connects a PostgreSQL adapter.
309
307
  def initialize(connection, logger, connection_parameters, config)
310
308
  super(connection, logger)
311
-
312
- connection_parameters.delete :prepared_statements
313
-
314
309
  @connection_parameters, @config = connection_parameters, config
310
+ @visitor = Arel::Visitors::PostgreSQL.new self
315
311
 
316
312
  # @local_tz is initialized as nil to avoid warnings when connect tries to use it
317
313
  @local_tz = nil
@@ -328,15 +324,6 @@ module ActiveRecord
328
324
  @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
329
325
  end
330
326
 
331
- def self.visitor_for(pool) # :nodoc:
332
- config = pool.spec.config
333
- if config.fetch(:prepared_statements) { true }
334
- Arel::Visitors::PostgreSQL.new pool
335
- else
336
- BindSubstitution.new pool
337
- end
338
- end
339
-
340
327
  # Clears the prepared statements cache.
341
328
  def clear_cache!
342
329
  @statements.clear
@@ -403,6 +390,11 @@ module ActiveRecord
403
390
  true
404
391
  end
405
392
 
393
+ # Returns true.
394
+ def supports_explain?
395
+ true
396
+ end
397
+
406
398
  # Returns the configured supported identifier length supported by PostgreSQL
407
399
  def table_alias_length
408
400
  @table_alias_length ||= query('SHOW max_identifier_length')[0][0].to_i
@@ -527,6 +519,48 @@ module ActiveRecord
527
519
 
528
520
  # DATABASE STATEMENTS ======================================
529
521
 
522
+ def explain(arel, binds = [])
523
+ sql = "EXPLAIN #{to_sql(arel)}"
524
+ ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
525
+ end
526
+
527
+ class ExplainPrettyPrinter # :nodoc:
528
+ # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
529
+ # PostgreSQL shell:
530
+ #
531
+ # QUERY PLAN
532
+ # ------------------------------------------------------------------------------
533
+ # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
534
+ # Join Filter: (posts.user_id = users.id)
535
+ # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
536
+ # Index Cond: (id = 1)
537
+ # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
538
+ # Filter: (posts.user_id = 1)
539
+ # (6 rows)
540
+ #
541
+ def pp(result)
542
+ header = result.columns.first
543
+ lines = result.rows.map(&:first)
544
+
545
+ # We add 2 because there's one char of padding at both sides, note
546
+ # the extra hyphens in the example above.
547
+ width = [header, *lines].map(&:length).max + 2
548
+
549
+ pp = []
550
+
551
+ pp << header.center(width).rstrip
552
+ pp << '-' * width
553
+
554
+ pp += lines.map {|line| " #{line}"}
555
+
556
+ nrows = result.rows.length
557
+ rows_label = nrows == 1 ? 'row' : 'rows'
558
+ pp << "(#{nrows} #{rows_label})"
559
+
560
+ pp.join("\n") + "\n"
561
+ end
562
+ end
563
+
530
564
  # Executes a SELECT query and returns an array of rows. Each row is an
531
565
  # array of field values.
532
566
  def select_rows(sql, name = nil)
@@ -535,10 +569,11 @@ module ActiveRecord
535
569
 
536
570
  # Executes an INSERT query and returns the new record's ID
537
571
  def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
538
- # Extract the table from the insert sql. Yuck.
539
- _, table = extract_schema_and_table(sql.split(" ", 4)[2])
540
-
541
- pk ||= primary_key(table)
572
+ unless pk
573
+ # Extract the table from the insert sql. Yuck.
574
+ table_ref = extract_table_ref_from_insert_sql(sql)
575
+ pk = primary_key(table_ref) if table_ref
576
+ end
542
577
 
543
578
  if pk
544
579
  select_value("#{sql} RETURNING #{quote_column_name(pk)}")
@@ -607,7 +642,7 @@ module ActiveRecord
607
642
  end
608
643
 
609
644
  def substitute_at(column, index)
610
- Arel::Nodes::BindParam.new "$#{index + 1}"
645
+ Arel.sql("$#{index + 1}")
611
646
  end
612
647
 
613
648
  def exec_query(sql, name = 'SQL', binds = [])
@@ -634,9 +669,9 @@ module ActiveRecord
634
669
 
635
670
  def sql_for_insert(sql, pk, id_value, sequence_name, binds)
636
671
  unless pk
637
- _, table = extract_schema_and_table(sql.split(" ", 4)[2])
638
-
639
- pk = primary_key(table)
672
+ # Extract the table from the insert sql. Yuck.
673
+ table_ref = extract_table_ref_from_insert_sql(sql)
674
+ pk = primary_key(table_ref) if table_ref
640
675
  end
641
676
 
642
677
  sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
@@ -682,12 +717,14 @@ module ActiveRecord
682
717
 
683
718
  # SCHEMA STATEMENTS ========================================
684
719
 
685
- def recreate_database(name) #:nodoc:
720
+ # Drops the database specified on the +name+ attribute
721
+ # and creates it again using the provided +options+.
722
+ def recreate_database(name, options = {}) #:nodoc:
686
723
  drop_database(name)
687
- create_database(name)
724
+ create_database(name, options)
688
725
  end
689
726
 
690
- # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
727
+ # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
691
728
  # <tt>:encoding</tt>, <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
692
729
  # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
693
730
  #
@@ -734,41 +771,39 @@ module ActiveRecord
734
771
  SQL
735
772
  end
736
773
 
774
+ # Returns true if table exists.
775
+ # If the schema is not specified as part of +name+ then it will only find tables within
776
+ # the current schema search path (regardless of permissions to access tables in other schemas)
737
777
  def table_exists?(name)
738
- schema, table = extract_schema_and_table(name.to_s)
739
- return false unless table # Abstract classes is having nil table name
778
+ schema, table = Utils.extract_schema_and_table(name.to_s)
779
+ return false unless table
740
780
 
741
- binds = [[nil, table.gsub(/(^"|"$)/,'')]]
781
+ binds = [[nil, table]]
742
782
  binds << [nil, schema] if schema
743
783
 
744
784
  exec_query(<<-SQL, 'SCHEMA', binds).rows.first[0].to_i > 0
745
785
  SELECT COUNT(*)
746
- FROM pg_tables
747
- WHERE tablename = $1
748
- AND schemaname = #{schema ? "$2" : "ANY (current_schemas(false))"}
786
+ FROM pg_class c
787
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
788
+ WHERE c.relkind in ('v','r')
789
+ AND c.relname = $1
790
+ AND n.nspname = #{schema ? '$2' : 'ANY (current_schemas(false))'}
749
791
  SQL
750
792
  end
751
793
 
752
- # Extracts the table and schema name from +name+
753
- def extract_schema_and_table(name)
754
- schema, table = name.split('.', 2)
755
-
756
- unless table # A table was provided without a schema
757
- table = schema
758
- schema = nil
759
- end
760
-
761
- if name =~ /^"/ # Handle quoted table names
762
- table = name
763
- schema = nil
764
- end
765
- [schema, table]
794
+ # Returns true if schema exists.
795
+ def schema_exists?(name)
796
+ exec_query(<<-SQL, 'SCHEMA', [[nil, name]]).rows.first[0].to_i > 0
797
+ SELECT COUNT(*)
798
+ FROM pg_namespace
799
+ WHERE nspname = $1
800
+ SQL
766
801
  end
767
802
 
768
803
  # Returns an array of indexes for the given table.
769
804
  def indexes(table_name, name = nil)
770
805
  result = query(<<-SQL, name)
771
- SELECT distinct i.relname, d.indisunique, d.indkey, t.oid
806
+ SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
772
807
  FROM pg_class t
773
808
  INNER JOIN pg_index d ON t.oid = d.indrelid
774
809
  INNER JOIN pg_class i ON d.indexrelid = i.oid
@@ -784,7 +819,8 @@ module ActiveRecord
784
819
  index_name = row[0]
785
820
  unique = row[1] == 't'
786
821
  indkey = row[2].split(" ")
787
- oid = row[3]
822
+ inddef = row[3]
823
+ oid = row[4]
788
824
 
789
825
  columns = Hash[query(<<-SQL, "Columns for index #{row[0]} on #{table_name}")]
790
826
  SELECT a.attnum, a.attname
@@ -794,7 +830,12 @@ module ActiveRecord
794
830
  SQL
795
831
 
796
832
  column_names = columns.values_at(*indkey).compact
797
- column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names)
833
+
834
+ # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
835
+ desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
836
+ orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
837
+
838
+ column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
798
839
  end.compact
799
840
  end
800
841
 
@@ -811,6 +852,11 @@ module ActiveRecord
811
852
  query('select current_database()')[0][0]
812
853
  end
813
854
 
855
+ # Returns the current schema name.
856
+ def current_schema
857
+ query('SELECT current_schema', 'SCHEMA')[0][0]
858
+ end
859
+
814
860
  # Returns the current database encoding format.
815
861
  def encoding
816
862
  query(<<-end_sql)[0][0]
@@ -833,7 +879,7 @@ module ActiveRecord
833
879
 
834
880
  # Returns the active schema search path.
835
881
  def schema_search_path
836
- @schema_search_path ||= query('SHOW search_path')[0][0]
882
+ @schema_search_path ||= query('SHOW search_path', 'SCHEMA')[0][0]
837
883
  end
838
884
 
839
885
  # Returns the current client message level.
@@ -874,7 +920,7 @@ module ActiveRecord
874
920
  end
875
921
 
876
922
  if pk && sequence
877
- quoted_sequence = quote_column_name(sequence)
923
+ quoted_sequence = quote_table_name(sequence)
878
924
 
879
925
  select_value <<-end_sql, 'Reset sequence'
880
926
  SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
@@ -886,46 +932,26 @@ module ActiveRecord
886
932
  def pk_and_sequence_for(table) #:nodoc:
887
933
  # First try looking for a sequence with a dependency on the
888
934
  # given table's primary key.
889
- result = query(<<-end_sql, 'PK and serial sequence')[0]
890
- SELECT attr.attname, seq.relname
891
- FROM pg_class seq,
892
- pg_attribute attr,
893
- pg_depend dep,
894
- pg_namespace name,
895
- pg_constraint cons
896
- WHERE seq.oid = dep.objid
897
- AND seq.relkind = 'S'
898
- AND attr.attrelid = dep.refobjid
899
- AND attr.attnum = dep.refobjsubid
900
- AND attr.attrelid = cons.conrelid
901
- AND attr.attnum = cons.conkey[1]
902
- AND cons.contype = 'p'
903
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
935
+ result = exec_query(<<-end_sql, 'SCHEMA').rows.first
936
+ SELECT attr.attname, ns.nspname, seq.relname
937
+ FROM pg_class seq
938
+ INNER JOIN pg_depend dep ON seq.oid = dep.objid
939
+ INNER JOIN pg_attribute attr ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
940
+ INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
941
+ INNER JOIN pg_namespace ns ON seq.relnamespace = ns.oid
942
+ WHERE seq.relkind = 'S'
943
+ AND cons.contype = 'p'
944
+ AND dep.refobjid = '#{quote_table_name(table)}'::regclass
904
945
  end_sql
905
946
 
906
- if result.nil? or result.empty?
907
- # If that fails, try parsing the primary key's default value.
908
- # Support the 7.x and 8.0 nextval('foo'::text) as well as
909
- # the 8.1+ nextval('foo'::regclass).
910
- result = query(<<-end_sql, 'PK and custom sequence')[0]
911
- SELECT attr.attname,
912
- CASE
913
- WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
914
- substr(split_part(def.adsrc, '''', 2),
915
- strpos(split_part(def.adsrc, '''', 2), '.')+1)
916
- ELSE split_part(def.adsrc, '''', 2)
917
- END
918
- FROM pg_class t
919
- JOIN pg_attribute attr ON (t.oid = attrelid)
920
- JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
921
- JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
922
- WHERE t.oid = '#{quote_table_name(table)}'::regclass
923
- AND cons.contype = 'p'
924
- AND def.adsrc ~* 'nextval'
925
- end_sql
947
+ # [primary_key, sequence]
948
+ if result.second == 'public' then
949
+ sequence = result.last
950
+ else
951
+ sequence = result.second+'.'+result.last
926
952
  end
927
953
 
928
- [result.first, result.last]
954
+ [result.first, sequence]
929
955
  rescue
930
956
  nil
931
957
  end
@@ -1037,6 +1063,25 @@ module ActiveRecord
1037
1063
  "DISTINCT #{columns}, #{order_columns * ', '}"
1038
1064
  end
1039
1065
 
1066
+ module Utils
1067
+ extend self
1068
+
1069
+ # Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
1070
+ # +schema_name+ is nil if not specified in +name+.
1071
+ # +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
1072
+ # +name+ supports the range of schema/table references understood by PostgreSQL, for example:
1073
+ #
1074
+ # * <tt>table_name</tt>
1075
+ # * <tt>"table.name"</tt>
1076
+ # * <tt>schema_name.table_name</tt>
1077
+ # * <tt>schema_name."table.name"</tt>
1078
+ # * <tt>"schema.name"."table name"</tt>
1079
+ def extract_schema_and_table(name)
1080
+ table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
1081
+ [schema, table]
1082
+ end
1083
+ end
1084
+
1040
1085
  protected
1041
1086
  # Returns the version of the connected PostgreSQL server.
1042
1087
  def postgresql_version
@@ -1203,9 +1248,14 @@ module ActiveRecord
1203
1248
  end
1204
1249
  end
1205
1250
 
1206
- def table_definition
1207
- TableDefinition.new(self)
1208
- end
1251
+ def extract_table_ref_from_insert_sql(sql)
1252
+ sql[/into\s+([^\(]*).*values\s*\(/i]
1253
+ $1.strip if $1
1254
+ end
1255
+
1256
+ def table_definition
1257
+ TableDefinition.new(self)
1258
+ end
1209
1259
  end
1210
1260
  end
1211
1261
  end
@@ -0,0 +1,50 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class SchemaCache
4
+ attr_reader :columns, :columns_hash, :primary_keys, :tables
5
+ attr_reader :connection
6
+
7
+ def initialize(conn)
8
+ @connection = conn
9
+ @tables = {}
10
+
11
+ @columns = Hash.new do |h, table_name|
12
+ h[table_name] = conn.columns(table_name, "#{table_name} Columns")
13
+ end
14
+
15
+ @columns_hash = Hash.new do |h, table_name|
16
+ h[table_name] = Hash[columns[table_name].map { |col|
17
+ [col.name, col]
18
+ }]
19
+ end
20
+
21
+ @primary_keys = Hash.new do |h, table_name|
22
+ h[table_name] = table_exists?(table_name) ? conn.primary_key(table_name) : nil
23
+ end
24
+ end
25
+
26
+ # A cached lookup for table existence.
27
+ def table_exists?(name)
28
+ return @tables[name] if @tables.key? name
29
+
30
+ @tables[name] = connection.table_exists?(name)
31
+ end
32
+
33
+ # Clears out internal caches
34
+ def clear!
35
+ @columns.clear
36
+ @columns_hash.clear
37
+ @primary_keys.clear
38
+ @tables.clear
39
+ end
40
+
41
+ # Clear out internal caches for table with +table_name+.
42
+ def clear_table_cache!(table_name)
43
+ @columns.delete table_name
44
+ @columns_hash.delete table_name
45
+ @primary_keys.delete table_name
46
+ @tables.delete table_name
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,6 +1,6 @@
1
1
  require 'active_record/connection_adapters/sqlite_adapter'
2
2
 
3
- gem 'sqlite3', '~> 1.3.4'
3
+ gem 'sqlite3', '~> 1.3.5'
4
4
  require 'sqlite3'
5
5
 
6
6
  module ActiveRecord
@@ -47,11 +47,7 @@ module ActiveRecord
47
47
 
48
48
  # Returns the current database encoding format as a string, eg: 'UTF-8'
49
49
  def encoding
50
- if @connection.respond_to?(:encoding)
51
- @connection.encoding.to_s
52
- else
53
- @connection.execute('PRAGMA encoding')[0]['encoding']
54
- end
50
+ @connection.encoding.to_s
55
51
  end
56
52
 
57
53
  end