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.
- data/CHANGELOG.md +6263 -103
- data/README.rdoc +2 -2
- data/examples/performance.rb +55 -31
- data/lib/active_record.rb +28 -2
- data/lib/active_record/aggregations.rb +2 -2
- data/lib/active_record/associations.rb +82 -69
- data/lib/active_record/associations/association.rb +2 -37
- data/lib/active_record/associations/association_scope.rb +3 -30
- data/lib/active_record/associations/builder/association.rb +6 -4
- data/lib/active_record/associations/builder/belongs_to.rb +3 -3
- data/lib/active_record/associations/builder/collection_association.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +5 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -16
- data/lib/active_record/associations/collection_association.rb +55 -28
- data/lib/active_record/associations/collection_proxy.rb +1 -35
- data/lib/active_record/associations/has_many_association.rb +5 -1
- data/lib/active_record/associations/has_many_through_association.rb +11 -8
- data/lib/active_record/associations/join_dependency.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +3 -1
- data/lib/active_record/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods.rb +212 -32
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
- data/lib/active_record/attribute_methods/dirty.rb +3 -3
- data/lib/active_record/attribute_methods/primary_key.rb +62 -25
- data/lib/active_record/attribute_methods/read.rb +69 -80
- data/lib/active_record/attribute_methods/serialization.rb +89 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -14
- data/lib/active_record/attribute_methods/write.rb +27 -5
- data/lib/active_record/autosave_association.rb +23 -8
- data/lib/active_record/base.rb +223 -1712
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +98 -132
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +82 -29
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +13 -42
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -13
- data/lib/active_record/connection_adapters/abstract_adapter.rb +78 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +138 -578
- data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -658
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +144 -94
- data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +43 -22
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +79 -0
- data/lib/active_record/errors.rb +11 -1
- data/lib/active_record/explain.rb +83 -0
- data/lib/active_record/explain_subscriber.rb +21 -0
- data/lib/active_record/fixtures.rb +31 -76
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/identity_map.rb +1 -7
- data/lib/active_record/inheritance.rb +167 -0
- data/lib/active_record/integration.rb +49 -0
- data/lib/active_record/locking/optimistic.rb +19 -11
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +3 -3
- data/lib/active_record/migration.rb +38 -29
- data/lib/active_record/migration/command_recorder.rb +7 -7
- data/lib/active_record/model_schema.rb +362 -0
- data/lib/active_record/nested_attributes.rb +3 -2
- data/lib/active_record/persistence.rb +51 -1
- data/lib/active_record/querying.rb +58 -0
- data/lib/active_record/railtie.rb +24 -28
- data/lib/active_record/railties/controller_runtime.rb +3 -1
- data/lib/active_record/railties/databases.rake +133 -77
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +7 -15
- data/lib/active_record/relation.rb +78 -35
- data/lib/active_record/relation/batches.rb +5 -2
- data/lib/active_record/relation/calculations.rb +27 -6
- data/lib/active_record/relation/delegation.rb +49 -0
- data/lib/active_record/relation/finder_methods.rb +5 -4
- data/lib/active_record/relation/predicate_builder.rb +13 -16
- data/lib/active_record/relation/query_methods.rb +59 -4
- data/lib/active_record/result.rb +1 -1
- data/lib/active_record/sanitization.rb +194 -0
- data/lib/active_record/schema_dumper.rb +5 -2
- data/lib/active_record/scoping.rb +152 -0
- data/lib/active_record/scoping/default.rb +140 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/serialization.rb +1 -43
- data/lib/active_record/serializers/xml_serializer.rb +2 -44
- data/lib/active_record/session_store.rb +11 -11
- data/lib/active_record/store.rb +50 -0
- data/lib/active_record/test_case.rb +11 -7
- data/lib/active_record/timestamp.rb +16 -3
- data/lib/active_record/transactions.rb +5 -5
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/validations/associated.rb +5 -4
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/version.rb +3 -3
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
- metadata +48 -38
- checksums.yaml +0 -7
- 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.
|
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
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
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
|
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
|
-
|
638
|
-
|
639
|
-
pk = primary_key(
|
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
|
-
|
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.
|
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
|
778
|
+
schema, table = Utils.extract_schema_and_table(name.to_s)
|
779
|
+
return false unless table
|
740
780
|
|
741
|
-
binds = [[nil, table
|
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
|
747
|
-
|
748
|
-
|
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
|
-
#
|
753
|
-
def
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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 =
|
890
|
-
SELECT attr.attname, seq.relname
|
891
|
-
FROM pg_class
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
WHERE seq.
|
897
|
-
AND
|
898
|
-
AND
|
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
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
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,
|
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
|
-
|
1207
|
-
|
1208
|
-
|
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.
|
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
|
-
|
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
|