colincasey-sequel 2.10.1 → 2.10.2
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.
- data/CHANGELOG +10 -0
- data/lib/sequel_core/adapters/ado.rb +3 -0
- data/lib/sequel_core/adapters/db2.rb +0 -11
- data/lib/sequel_core/adapters/dbi.rb +0 -11
- data/lib/sequel_core/adapters/do.rb +0 -12
- data/lib/sequel_core/adapters/firebird.rb +19 -14
- data/lib/sequel_core/adapters/informix.rb +1 -11
- data/lib/sequel_core/adapters/jdbc/h2.rb +3 -11
- data/lib/sequel_core/adapters/jdbc/mysql.rb +0 -10
- data/lib/sequel_core/adapters/jdbc/postgresql.rb +3 -15
- data/lib/sequel_core/adapters/jdbc.rb +1 -13
- data/lib/sequel_core/adapters/mysql.rb +6 -13
- data/lib/sequel_core/adapters/odbc.rb +34 -28
- data/lib/sequel_core/adapters/openbase.rb +0 -11
- data/lib/sequel_core/adapters/oracle.rb +11 -9
- data/lib/sequel_core/adapters/postgres.rb +13 -16
- data/lib/sequel_core/adapters/shared/ms_access.rb +8 -2
- data/lib/sequel_core/adapters/shared/mssql.rb +6 -15
- data/lib/sequel_core/adapters/shared/mysql.rb +23 -14
- data/lib/sequel_core/adapters/shared/oracle.rb +4 -0
- data/lib/sequel_core/adapters/shared/postgres.rb +27 -25
- data/lib/sequel_core/adapters/shared/progress.rb +4 -0
- data/lib/sequel_core/adapters/shared/sqlite.rb +9 -16
- data/lib/sequel_core/adapters/sqlite.rb +5 -14
- data/lib/sequel_core/core_sql.rb +7 -3
- data/lib/sequel_core/dataset/convenience.rb +1 -1
- data/lib/sequel_core/dataset/prepared_statements.rb +1 -1
- data/lib/sequel_core/dataset/sql.rb +116 -30
- data/lib/sequel_core/dataset.rb +2 -2
- data/lib/sequel_core/sql.rb +2 -31
- data/lib/sequel_model/base.rb +23 -7
- data/spec/integration/dataset_test.rb +2 -2
- data/spec/sequel_core/core_sql_spec.rb +9 -0
- data/spec/sequel_core/dataset_spec.rb +27 -31
- data/spec/sequel_model/base_spec.rb +66 -0
- data/spec/sequel_model/spec_helper.rb +3 -0
- metadata +1 -1
- data/lib/sequel_core/dataset/stored_procedures.rb +0 -75
- data/lib/sequel_core/dataset/unsupported.rb +0 -43
@@ -161,7 +161,7 @@ module Sequel
|
|
161
161
|
|
162
162
|
# Methods shared by Database instances that connect to PostgreSQL.
|
163
163
|
module DatabaseMethods
|
164
|
-
PREPARED_ARG_PLACEHOLDER = '$'.
|
164
|
+
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
165
165
|
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
|
166
166
|
SQL_BEGIN = 'BEGIN'.freeze
|
167
167
|
SQL_SAVEPOINT = 'SAVEPOINT autopoint_%d'.freeze
|
@@ -550,6 +550,7 @@ module Sequel
|
|
550
550
|
FOR_SHARE = ' FOR SHARE'.freeze
|
551
551
|
FOR_UPDATE = ' FOR UPDATE'.freeze
|
552
552
|
LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
|
553
|
+
NULL = LiteralString.new('NULL').freeze
|
553
554
|
PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
|
554
555
|
QUERY_PLAN = 'QUERY PLAN'.to_sym
|
555
556
|
ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
|
@@ -629,29 +630,6 @@ module Sequel
|
|
629
630
|
single_record(default_server_opts(:naked=>true, :sql=>insert_returning_sql(nil, *values))) if server_version >= 80200
|
630
631
|
end
|
631
632
|
|
632
|
-
# Handle microseconds for Time and DateTime values, as well as PostgreSQL
|
633
|
-
# specific boolean values and string escaping.
|
634
|
-
def literal(v)
|
635
|
-
case v
|
636
|
-
when LiteralString
|
637
|
-
v
|
638
|
-
when SQL::Blob
|
639
|
-
"'#{v.gsub(/[\000-\037\047\134\177-\377]/){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"}}'"
|
640
|
-
when String
|
641
|
-
"'#{v.gsub("'", "''")}'"
|
642
|
-
when Time
|
643
|
-
"#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d",v.usec)}'"
|
644
|
-
when DateTime
|
645
|
-
"#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d", (v.sec_fraction * 86400000000).to_i)}'"
|
646
|
-
when TrueClass
|
647
|
-
BOOL_TRUE
|
648
|
-
when FalseClass
|
649
|
-
BOOL_FALSE
|
650
|
-
else
|
651
|
-
super
|
652
|
-
end
|
653
|
-
end
|
654
|
-
|
655
633
|
# Locks the table with the specified mode.
|
656
634
|
def lock(mode, server=nil)
|
657
635
|
sql = LOCK % [source_list(@opts[:from]), mode]
|
@@ -679,9 +657,33 @@ module Sequel
|
|
679
657
|
# Use the RETURNING clause to return the primary key of the inserted record, if it exists
|
680
658
|
def insert_returning_pk_sql(*values)
|
681
659
|
pk = db.primary_key(opts[:from].first)
|
682
|
-
insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) :
|
660
|
+
insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : NULL, *values)
|
683
661
|
end
|
684
662
|
|
663
|
+
def literal_blob(v)
|
664
|
+
"'#{v.gsub(/[\000-\037\047\134\177-\377]/){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"}}'"
|
665
|
+
end
|
666
|
+
|
667
|
+
def literal_datetime(v)
|
668
|
+
"#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d", (v.sec_fraction * 86400000000).to_i)}'"
|
669
|
+
end
|
670
|
+
|
671
|
+
def literal_false
|
672
|
+
BOOL_FALSE
|
673
|
+
end
|
674
|
+
|
675
|
+
def literal_string(v)
|
676
|
+
"'#{v.gsub("'", "''")}'"
|
677
|
+
end
|
678
|
+
|
679
|
+
def literal_true
|
680
|
+
BOOL_TRUE
|
681
|
+
end
|
682
|
+
|
683
|
+
def literal_time(v)
|
684
|
+
"#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d",v.usec)}'"
|
685
|
+
end
|
686
|
+
|
685
687
|
# PostgreSQL is smart and can use parantheses around all datasets to get
|
686
688
|
# the correct answers.
|
687
689
|
def select_compounds_sql(sql, opts)
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'sequel_core/adapters/utils/date_format'
|
2
|
+
require 'sequel_core/adapters/utils/unsupported'
|
3
|
+
|
1
4
|
module Sequel
|
2
5
|
module Progress
|
3
6
|
module DatabaseMethods
|
@@ -11,6 +14,7 @@ module Sequel
|
|
11
14
|
|
12
15
|
module DatasetMethods
|
13
16
|
include Dataset::UnsupportedIntersectExcept
|
17
|
+
include Dataset::SQLStandardDateFormat
|
14
18
|
|
15
19
|
SELECT_CLAUSE_ORDER = %w'limit distinct columns from join where group order having compounds'.freeze
|
16
20
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'sequel_core/adapters/utils/unsupported'
|
2
|
+
|
1
3
|
module Sequel
|
2
4
|
module SQLite
|
3
5
|
module DatabaseMethods
|
@@ -142,7 +144,7 @@ module Sequel
|
|
142
144
|
# The array of column schema hashes, except for the ones given in opts[:except]
|
143
145
|
def defined_columns_for(table, opts={})
|
144
146
|
cols = parse_pragma(table, {})
|
145
|
-
cols.each{|c| c[:default] = c[:default]
|
147
|
+
cols.each{|c| c[:default] = LiteralString.new(c[:default]) if c[:default]}
|
146
148
|
if opts[:except]
|
147
149
|
nono= Array(opts[:except]).compact.map{|n| n.to_s}
|
148
150
|
cols.reject!{|c| nono.include? c[:name] }
|
@@ -234,27 +236,18 @@ module Sequel
|
|
234
236
|
end
|
235
237
|
end
|
236
238
|
|
237
|
-
def literal(v)
|
238
|
-
case v
|
239
|
-
when ::Sequel::SQL::Blob
|
240
|
-
blob = ''
|
241
|
-
v.each_byte{|x| blob << sprintf('%02x', x)}
|
242
|
-
"X'#{blob}'"
|
243
|
-
when Time
|
244
|
-
literal(v.iso8601)
|
245
|
-
when Date, DateTime
|
246
|
-
literal(v.to_s)
|
247
|
-
else
|
248
|
-
super
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
239
|
# SQLite uses the nonstandard ` (backtick) for quoting identifiers.
|
253
240
|
def quoted_identifier(c)
|
254
241
|
"`#{c}`"
|
255
242
|
end
|
256
243
|
|
257
244
|
private
|
245
|
+
|
246
|
+
def literal_blob(v)
|
247
|
+
blob = ''
|
248
|
+
v.each_byte{|x| blob << sprintf('%02x', x)}
|
249
|
+
"X'#{blob}'"
|
250
|
+
end
|
258
251
|
|
259
252
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
260
253
|
def as_sql(expression, aliaz)
|
@@ -159,7 +159,7 @@ module Sequel
|
|
159
159
|
# SQLite uses a : before the name of the argument for named
|
160
160
|
# arguments.
|
161
161
|
def prepared_arg(k)
|
162
|
-
"#{prepared_arg_placeholder}#{k}"
|
162
|
+
LiteralString.new("#{prepared_arg_placeholder}#{k}")
|
163
163
|
end
|
164
164
|
end
|
165
165
|
|
@@ -214,19 +214,6 @@ module Sequel
|
|
214
214
|
end
|
215
215
|
end
|
216
216
|
|
217
|
-
# Use the ISO format for dates and timestamps, and quote strings
|
218
|
-
# using the ::SQLite3::Database.quote method.
|
219
|
-
def literal(v)
|
220
|
-
case v
|
221
|
-
when LiteralString, ::Sequel::SQL::Blob
|
222
|
-
super
|
223
|
-
when String
|
224
|
-
"'#{::SQLite3::Database.quote(v)}'"
|
225
|
-
else
|
226
|
-
super
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
217
|
# Prepare the given type of query with the given name and store
|
231
218
|
# it in the database. Note that a new native prepared statement is
|
232
219
|
# created on each call to this prepared statement.
|
@@ -239,6 +226,10 @@ module Sequel
|
|
239
226
|
|
240
227
|
private
|
241
228
|
|
229
|
+
def literal_string(v)
|
230
|
+
"'#{::SQLite3::Database.quote(v)}'"
|
231
|
+
end
|
232
|
+
|
242
233
|
# SQLite uses a : before the name of the argument as a placeholder.
|
243
234
|
def prepared_arg_placeholder
|
244
235
|
PREPARED_ARG_PLACEHOLDER
|
data/lib/sequel_core/core_sql.rb
CHANGED
@@ -121,7 +121,7 @@ class String
|
|
121
121
|
include Sequel::SQL::AliasMethods
|
122
122
|
include Sequel::SQL::CastMethods
|
123
123
|
|
124
|
-
# Converts a string into
|
124
|
+
# Converts a string into a Sequel::LiteralString, in order to override string
|
125
125
|
# literalization, e.g.:
|
126
126
|
#
|
127
127
|
# DB[:items].filter(:abc => 'def').sql #=>
|
@@ -130,8 +130,12 @@ class String
|
|
130
130
|
# DB[:items].filter(:abc => 'def'.lit).sql #=>
|
131
131
|
# "SELECT * FROM items WHERE (abc = def)"
|
132
132
|
#
|
133
|
-
|
134
|
-
|
133
|
+
# You can also provide arguments, to create a Sequel::SQL::PlaceholderLiteralString:
|
134
|
+
#
|
135
|
+
# DB[:items].select{|o| o.count('DISTINCT ?'.lit(:a))}.sql #=>
|
136
|
+
# "SELECT count(DISTINCT a) FROM items"
|
137
|
+
def lit(*args)
|
138
|
+
args.empty? ? Sequel::LiteralString.new(self) : Sequel::SQL::PlaceholderLiteralString.new(self, args)
|
135
139
|
end
|
136
140
|
alias_method :expr, :lit
|
137
141
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Sequel
|
2
2
|
class Dataset
|
3
3
|
COMMA_SEPARATOR = ', '.freeze
|
4
|
-
COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, '*'.
|
4
|
+
COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, LiteralString.new('*'.freeze)).as(:count)
|
5
5
|
|
6
6
|
# Returns the first record matching the conditions.
|
7
7
|
def [](*conditions)
|
@@ -7,13 +7,11 @@ module Sequel
|
|
7
7
|
COLUMN_REF_RE2 = /\A([\w ]+)___([\w ]+)\z/.freeze
|
8
8
|
COLUMN_REF_RE3 = /\A([\w ]+)__([\w ]+)\z/.freeze
|
9
9
|
COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
|
10
|
-
DATE_FORMAT = "DATE '%Y-%m-%d'".freeze
|
11
10
|
N_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::N_ARITY_OPERATORS
|
12
11
|
NULL = "NULL".freeze
|
13
12
|
QUESTION_MARK = '?'.freeze
|
14
|
-
STOCK_COUNT_OPTS = {:select => ["COUNT(*)".
|
13
|
+
STOCK_COUNT_OPTS = {:select => [LiteralString.new("COUNT(*)").freeze], :order => nil}.freeze
|
15
14
|
SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
|
16
|
-
TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S'".freeze
|
17
15
|
TWO_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::TWO_ARITY_OPERATORS
|
18
16
|
WILDCARD = '*'.freeze
|
19
17
|
|
@@ -131,7 +129,7 @@ module Sequel
|
|
131
129
|
# DB.select(1).where(DB[:items].exists).sql
|
132
130
|
# #=> "SELECT 1 WHERE EXISTS (SELECT * FROM items)"
|
133
131
|
def exists(opts = nil)
|
134
|
-
"EXISTS (#{select_sql(opts)})"
|
132
|
+
LiteralString.new("EXISTS (#{select_sql(opts)})")
|
135
133
|
end
|
136
134
|
|
137
135
|
# Returns a copy of the dataset with the given conditions imposed upon it.
|
@@ -468,38 +466,39 @@ module Sequel
|
|
468
466
|
# If an unsupported object is given, an exception is raised.
|
469
467
|
def literal(v)
|
470
468
|
case v
|
471
|
-
when LiteralString
|
472
|
-
v
|
473
469
|
when String
|
474
|
-
|
475
|
-
|
476
|
-
|
470
|
+
return v if v.is_a?(LiteralString)
|
471
|
+
v.is_a?(SQL::Blob) ? literal_blob(v) : literal_string(v)
|
472
|
+
when Symbol
|
473
|
+
literal_symbol(v)
|
474
|
+
when Integer
|
475
|
+
literal_integer(v)
|
476
|
+
when Hash
|
477
|
+
literal_hash(v)
|
478
|
+
when SQL::Expression
|
479
|
+
literal_expression(v)
|
480
|
+
when Float
|
481
|
+
literal_float(v)
|
477
482
|
when BigDecimal
|
478
|
-
|
479
|
-
d = "'#{d}'" if v.nan? || v.infinite?
|
480
|
-
d
|
483
|
+
literal_big_decimal(v)
|
481
484
|
when NilClass
|
482
485
|
NULL
|
483
486
|
when TrueClass
|
484
|
-
|
487
|
+
literal_true
|
485
488
|
when FalseClass
|
486
|
-
|
487
|
-
when Symbol
|
488
|
-
symbol_to_column_ref(v)
|
489
|
-
when ::Sequel::SQL::Expression
|
490
|
-
v.to_s(self)
|
489
|
+
literal_false
|
491
490
|
when Array
|
492
|
-
|
493
|
-
when
|
494
|
-
|
495
|
-
when
|
496
|
-
v
|
491
|
+
literal_array(v)
|
492
|
+
when Time
|
493
|
+
literal_time(v)
|
494
|
+
when DateTime
|
495
|
+
literal_datetime(v)
|
497
496
|
when Date
|
498
|
-
v
|
497
|
+
literal_date(v)
|
499
498
|
when Dataset
|
500
|
-
|
499
|
+
literal_dataset(v)
|
501
500
|
else
|
502
|
-
|
501
|
+
literal_other(v)
|
503
502
|
end
|
504
503
|
end
|
505
504
|
|
@@ -674,9 +673,7 @@ module Sequel
|
|
674
673
|
# :items__abc___a.to_column_ref(ds) #=> "items.abc AS a"
|
675
674
|
#
|
676
675
|
def symbol_to_column_ref(sym)
|
677
|
-
|
678
|
-
qc = "#{"#{quote_identifier(c_table)}." if c_table}#{quote_identifier(column)}"
|
679
|
-
c_alias ? as_sql(qc, c_alias) : qc
|
676
|
+
literal_symbol(sym)
|
680
677
|
end
|
681
678
|
|
682
679
|
# Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
|
@@ -745,6 +742,12 @@ module Sequel
|
|
745
742
|
sql
|
746
743
|
end
|
747
744
|
|
745
|
+
# Returns a copy of the dataset with the static SQL used. This is useful if you want
|
746
|
+
# to keep the same row_proc/transform/graph, but change the SQL used to custom SQL.
|
747
|
+
def with_sql(sql)
|
748
|
+
clone(:sql=>sql)
|
749
|
+
end
|
750
|
+
|
748
751
|
[:inner, :full_outer, :right_outer, :left_outer].each do |jtype|
|
749
752
|
class_eval("def #{jtype}_join(*args, &block); join_table(:#{jtype}, *args, &block) end")
|
750
753
|
end
|
@@ -816,7 +819,7 @@ module Sequel
|
|
816
819
|
when TrueClass, FalseClass
|
817
820
|
SQL::BooleanExpression.new(:NOOP, expr)
|
818
821
|
when String
|
819
|
-
"(#{expr})"
|
822
|
+
LiteralString.new("(#{expr})")
|
820
823
|
else
|
821
824
|
raise(Error, 'Invalid filter argument')
|
822
825
|
end
|
@@ -857,6 +860,89 @@ module Sequel
|
|
857
860
|
"#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
|
858
861
|
end
|
859
862
|
|
863
|
+
# SQL fragment for Array. Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
|
864
|
+
def literal_array(v)
|
865
|
+
v.all_two_pairs? ? literal_expression(v.sql_expr) : array_sql(v)
|
866
|
+
end
|
867
|
+
|
868
|
+
# SQL fragment for BigDecimal
|
869
|
+
def literal_big_decimal(v)
|
870
|
+
d = v.to_s("F")
|
871
|
+
v.nan? || v.infinite? ? "'#{d}'" : d
|
872
|
+
end
|
873
|
+
|
874
|
+
# SQL fragment for SQL::Blob
|
875
|
+
def literal_blob(v)
|
876
|
+
literal_string(v)
|
877
|
+
end
|
878
|
+
|
879
|
+
# SQL fragment for Dataset. Does a subselect inside parantheses.
|
880
|
+
def literal_dataset(v)
|
881
|
+
"(#{subselect_sql(v)})"
|
882
|
+
end
|
883
|
+
|
884
|
+
# SQL fragment for Date, using the ISO8601 format.
|
885
|
+
def literal_date(v)
|
886
|
+
"'#{v}'"
|
887
|
+
end
|
888
|
+
|
889
|
+
# SQL fragment for DateTime, using the ISO8601 format.
|
890
|
+
def literal_datetime(v)
|
891
|
+
"'#{v}'"
|
892
|
+
end
|
893
|
+
|
894
|
+
# SQL fragment for SQL::Expression, result depends on the specific type of expression.
|
895
|
+
def literal_expression(v)
|
896
|
+
v.to_s(self)
|
897
|
+
end
|
898
|
+
|
899
|
+
# SQL fragment for false
|
900
|
+
def literal_false
|
901
|
+
BOOL_FALSE
|
902
|
+
end
|
903
|
+
|
904
|
+
# SQL fragment for Float
|
905
|
+
def literal_float(v)
|
906
|
+
v.to_s
|
907
|
+
end
|
908
|
+
|
909
|
+
# SQL fragment for Hash, treated as an expression
|
910
|
+
def literal_hash(v)
|
911
|
+
literal_expression(v.sql_expr)
|
912
|
+
end
|
913
|
+
|
914
|
+
# SQL fragment for Integer
|
915
|
+
def literal_integer(v)
|
916
|
+
v.to_s
|
917
|
+
end
|
918
|
+
|
919
|
+
# SQL fragmento for a type of object not handled by Dataset#literal. Raises an error. If a database specific type is allowed, this should be overriden in a subclass.
|
920
|
+
def literal_other(v)
|
921
|
+
raise Error, "can't express #{v.inspect} as a SQL literal"
|
922
|
+
end
|
923
|
+
|
924
|
+
# SQL fragment for String. Doubles \ and ' by default.
|
925
|
+
def literal_string(v)
|
926
|
+
"'#{v.gsub(/\\/, "\\\\\\\\").gsub(/'/, "''")}'"
|
927
|
+
end
|
928
|
+
|
929
|
+
# SQL fragment for Symbol, treated as an identifier, possibly aliased and/or qualified.
|
930
|
+
def literal_symbol(v)
|
931
|
+
c_table, column, c_alias = split_symbol(v)
|
932
|
+
qc = "#{"#{quote_identifier(c_table)}." if c_table}#{quote_identifier(column)}"
|
933
|
+
c_alias ? as_sql(qc, c_alias) : qc
|
934
|
+
end
|
935
|
+
|
936
|
+
# SQL fragment for Time, uses the ISO8601 format.
|
937
|
+
def literal_time(v)
|
938
|
+
"'#{v.iso8601}'"
|
939
|
+
end
|
940
|
+
|
941
|
+
# SQL fragment for true.
|
942
|
+
def literal_true
|
943
|
+
BOOL_TRUE
|
944
|
+
end
|
945
|
+
|
860
946
|
# Returns a qualified column name (including a table name) if the column
|
861
947
|
# name isn't already qualified.
|
862
948
|
def qualified_column_name(column, table)
|
data/lib/sequel_core/dataset.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
%w'callback convenience pagination prepared_statements query schema sql
|
1
|
+
%w'callback convenience pagination prepared_statements query schema sql'.each do |f|
|
2
2
|
require "sequel_core/dataset/#{f}"
|
3
3
|
end
|
4
4
|
|
@@ -54,7 +54,7 @@ module Sequel
|
|
54
54
|
left_outer_join limit naked or order order_by order_more paginate query reject
|
55
55
|
reverse reverse_order right_outer_join select select_all select_more
|
56
56
|
set_defaults set_graph_aliases set_model set_overrides sort sort_by
|
57
|
-
unfiltered union unordered where'.collect{|x| x.to_sym}
|
57
|
+
unfiltered union unordered where with_sql'.collect{|x| x.to_sym}
|
58
58
|
|
59
59
|
NOTIMPL_MSG = "This method must be overridden in Sequel adapters".freeze
|
60
60
|
STOCK_TRANSFORMS = {
|
data/lib/sequel_core/sql.rb
CHANGED
@@ -336,12 +336,12 @@ module Sequel
|
|
336
336
|
module ComplexExpressionMethods
|
337
337
|
# Extract a datetime_part (e.g. year, month) from self:
|
338
338
|
#
|
339
|
-
# :date.extract(:year) # SQL: extract(year FROM date)
|
339
|
+
# :date.extract(:year) # SQL: extract(year FROM "date")
|
340
340
|
#
|
341
341
|
# Also has the benefit of returning the result as a
|
342
342
|
# NumericExpression instead of a generic ComplexExpression.
|
343
343
|
def extract(datetime_part)
|
344
|
-
|
344
|
+
Function.new(:extract, PlaceholderLiteralString.new("#{datetime_part} FROM ?", [self])).sql_number
|
345
345
|
end
|
346
346
|
|
347
347
|
# Return a BooleanExpression representation of self.
|
@@ -592,35 +592,6 @@ module Sequel
|
|
592
592
|
end
|
593
593
|
end
|
594
594
|
|
595
|
-
# IrregularFunction is used for the SQL EXTRACT function,
|
596
|
-
# which don't use regular function calling syntax. The IrregularFunction
|
597
|
-
# replaces the commas the regular function uses with a custom
|
598
|
-
# join string.
|
599
|
-
#
|
600
|
-
# This shouldn't be used directly, see
|
601
|
-
# ComplexExpressionMethods#extract.
|
602
|
-
class IrregularFunction < Function
|
603
|
-
# The arguments to pass to the function (may be blank)
|
604
|
-
attr_reader :arg1, :arg2
|
605
|
-
|
606
|
-
# The SQL function to call
|
607
|
-
attr_reader :f
|
608
|
-
|
609
|
-
# The literal string to use in place of a comma to join arguments
|
610
|
-
attr_reader :joiner
|
611
|
-
|
612
|
-
# Set the attributes to the given arguments
|
613
|
-
def initialize(f, arg1, joiner, arg2)
|
614
|
-
@f, @arg1, @joiner, @arg2 = f, arg1, joiner, arg2
|
615
|
-
end
|
616
|
-
|
617
|
-
# Delegate the creation of the resulting SQL to the given dataset,
|
618
|
-
# since it may be database dependent.
|
619
|
-
def to_s(ds)
|
620
|
-
ds.irregular_function_sql(self)
|
621
|
-
end
|
622
|
-
end
|
623
|
-
|
624
595
|
# Represents an SQL JOIN clause, used for joining tables.
|
625
596
|
class JoinClause < SpecificExpression
|
626
597
|
# The type of join to do
|
data/lib/sequel_model/base.rb
CHANGED
@@ -14,6 +14,8 @@ module Sequel
|
|
14
14
|
@raise_on_typecast_failure = true
|
15
15
|
@restrict_primary_key = true
|
16
16
|
@restricted_columns = nil
|
17
|
+
@simple_pk = nil
|
18
|
+
@simple_table = nil
|
17
19
|
@skip_superclass_validations = nil
|
18
20
|
@sti_dataset = nil
|
19
21
|
@sti_key = nil
|
@@ -48,6 +50,14 @@ module Sequel
|
|
48
50
|
# (default: no columns).
|
49
51
|
metaattr_reader :restricted_columns
|
50
52
|
|
53
|
+
# Should be the literal primary key column name if this Model's table has a simple primary key, or
|
54
|
+
# nil if the model has a compound primary key or no primary key.
|
55
|
+
metaattr_reader :simple_pk
|
56
|
+
|
57
|
+
# Should be the literal table name if this Model's dataset is a simple table (no select, order, join, etc.),
|
58
|
+
# or nil otherwise.
|
59
|
+
metaattr_reader :simple_table
|
60
|
+
|
51
61
|
# The base dataset for STI, to which filters are added to get
|
52
62
|
# only the models for the specific STI subclass.
|
53
63
|
metaattr_reader :sti_dataset
|
@@ -75,12 +85,13 @@ module Sequel
|
|
75
85
|
left_outer_join limit map multi_insert naked order order_by order_more
|
76
86
|
paginate print query range reverse_order right_outer_join select
|
77
87
|
select_all select_more server set set_graph_aliases single_value size to_csv to_hash
|
78
|
-
transform union uniq unfiltered unordered update where'.map{|x| x.to_sym}
|
88
|
+
transform union uniq unfiltered unordered update where with_sql'.map{|x| x.to_sym}
|
79
89
|
|
80
90
|
# Instance variables that are inherited in subclasses
|
81
91
|
INHERITED_INSTANCE_VARIABLES = {:@allowed_columns=>:dup, :@cache_store=>nil,
|
82
92
|
:@cache_ttl=>nil, :@dataset_methods=>:dup, :@primary_key=>nil,
|
83
93
|
:@raise_on_save_failure=>nil, :@restricted_columns=>:dup, :@restrict_primary_key=>nil,
|
94
|
+
:@simple_pk=>nil, :@simple_table=>nil,
|
84
95
|
:@sti_dataset=>nil, :@sti_key=>nil, :@strict_param_setting=>nil,
|
85
96
|
:@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil,
|
86
97
|
:@raise_on_typecast_failure=>nil, :@association_reflections=>:dup}
|
@@ -95,11 +106,12 @@ module Sequel
|
|
95
106
|
# first before a dataset lookup is attempted unless a hash is supplied.
|
96
107
|
def self.[](*args)
|
97
108
|
args = args.first if (args.size == 1)
|
98
|
-
|
99
|
-
|
100
|
-
|
109
|
+
return dataset[args] if args.is_a?(Hash)
|
110
|
+
return cache_lookup(args) if @cache_store
|
111
|
+
if t = simple_table and p = simple_pk
|
112
|
+
with_sql("SELECT * FROM #{t} WHERE #{p} = #{dataset.literal(args)} LIMIT 1").first
|
101
113
|
else
|
102
|
-
|
114
|
+
dataset[primary_key_hash(args)]
|
103
115
|
end
|
104
116
|
end
|
105
117
|
|
@@ -223,7 +235,7 @@ module Sequel
|
|
223
235
|
db
|
224
236
|
begin
|
225
237
|
if sup_class == Model
|
226
|
-
subclass.set_dataset(
|
238
|
+
subclass.set_dataset(subclass.implicit_table_name) unless subclass.name.blank?
|
227
239
|
elsif ds = sup_class.instance_variable_get(:@dataset)
|
228
240
|
subclass.set_dataset(sup_class.sti_key ? sup_class.sti_dataset.filter(sup_class.sti_key=>subclass.name.to_s) : ds.clone, :inherited=>true)
|
229
241
|
end
|
@@ -251,7 +263,7 @@ module Sequel
|
|
251
263
|
# Mark the model as not having a primary key. Not having a primary key
|
252
264
|
# can cause issues, among which is that you won't be able to update records.
|
253
265
|
def self.no_primary_key
|
254
|
-
@primary_key = nil
|
266
|
+
@simple_pk = @primary_key = nil
|
255
267
|
end
|
256
268
|
|
257
269
|
# Returns primary key attribute hash. If using a composite primary key
|
@@ -329,8 +341,10 @@ module Sequel
|
|
329
341
|
inherited = opts[:inherited]
|
330
342
|
@dataset = case ds
|
331
343
|
when Symbol
|
344
|
+
@simple_table = db.literal(ds)
|
332
345
|
db[ds]
|
333
346
|
when Dataset
|
347
|
+
@simple_table = nil
|
334
348
|
@db = ds.db
|
335
349
|
ds
|
336
350
|
else
|
@@ -339,6 +353,7 @@ module Sequel
|
|
339
353
|
@dataset.set_model(self)
|
340
354
|
@dataset.transform(@transform) if @transform
|
341
355
|
if inherited
|
356
|
+
@simple_table = sti_key ? nil : superclass.simple_table
|
342
357
|
@columns = @dataset.columns rescue nil
|
343
358
|
else
|
344
359
|
@dataset.extend(DatasetMethods)
|
@@ -366,6 +381,7 @@ module Sequel
|
|
366
381
|
# You can set it to nil to not have a primary key, but that
|
367
382
|
# cause certain things not to work, see #no_primary_key.
|
368
383
|
def self.set_primary_key(*key)
|
384
|
+
@simple_pk = key.length == 1 ? db.literal(key.first) : nil
|
369
385
|
@primary_key = (key.length == 1) ? key[0] : key.flatten
|
370
386
|
end
|
371
387
|
|
@@ -101,7 +101,7 @@ describe "Dataset UNION, EXCEPT, and INTERSECT" do
|
|
101
101
|
|
102
102
|
specify "should give the correct results for simple UNION, EXCEPT, and INTERSECT" do
|
103
103
|
@ds1.union(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30'
|
104
|
-
unless @ds1.class.ancestors.include?(Sequel::Dataset::UnsupportedIntersectExcept)
|
104
|
+
unless defined?(Sequel::Dataset::UnsupportedIntersectExcept) and @ds1.class.ancestors.include?(Sequel::Dataset::UnsupportedIntersectExcept)
|
105
105
|
@ds1.except(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'20'
|
106
106
|
@ds1.intersect(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'10'
|
107
107
|
end
|
@@ -110,7 +110,7 @@ describe "Dataset UNION, EXCEPT, and INTERSECT" do
|
|
110
110
|
specify "should give the correct results for compound UNION, EXCEPT, and INTERSECT" do
|
111
111
|
@ds1.union(@ds2).union(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30 40'
|
112
112
|
@ds1.union(@ds2.union(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30 40'
|
113
|
-
unless @ds1.class.ancestors.include?(Sequel::Dataset::UnsupportedIntersectExcept)
|
113
|
+
unless defined?(Sequel::Dataset::UnsupportedIntersectExcept) and @ds1.class.ancestors.include?(Sequel::Dataset::UnsupportedIntersectExcept)
|
114
114
|
@ds1.union(@ds2).except(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'20 30'
|
115
115
|
@ds1.union(@ds2.except(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30'
|
116
116
|
@ds1.union(@ds2).intersect(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 '
|
@@ -124,6 +124,15 @@ context "String#lit" do
|
|
124
124
|
Sequel::Database.new[:t].update_sql(:stamp => "NOW()".expr).should == \
|
125
125
|
"UPDATE t SET stamp = NOW()"
|
126
126
|
end
|
127
|
+
|
128
|
+
specify "should return a PlaceholderLiteralString object if args are given" do
|
129
|
+
a = 'DISTINCT ?'.lit(:a)
|
130
|
+
a.should be_a_kind_of(Sequel::SQL::PlaceholderLiteralString)
|
131
|
+
ds = MockDatabase.new.dataset
|
132
|
+
ds.literal(a).should == 'DISTINCT a'
|
133
|
+
ds.quote_identifiers = true
|
134
|
+
ds.literal(a).should == 'DISTINCT "a"'
|
135
|
+
end
|
127
136
|
end
|
128
137
|
|
129
138
|
context "String#to_blob and #to_sequel_blob" do
|