mini_sql 1.4.0 → 1.5.0

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: bbfef3c29b94c4d0bdbb461b52419644f3b042236f58837a6247a84546034e26
4
- data.tar.gz: 52b175f5f3c712f891f61fe47618584c4279e0bd9f4a0496fb545bd3574eb8ba
3
+ metadata.gz: 491de0ce7925ad9ec428409af4a78e5ce3f91b6f886bfcca7c17544a4b44e23a
4
+ data.tar.gz: 2269da4aa96ec6d58c2141b82cae6d3764664e312ada254fe7f45476e6cb5b55
5
5
  SHA512:
6
- metadata.gz: 00a003b8bdc6bdf623ad10d3702b3851b0c5bdbc5c1d82c76aa40c5dd797ef11d4bcb153d85cf323d451403d87fb90d0b12c43da3df84a5c49aa84b1d8c8c570
7
- data.tar.gz: 11fd1b56b47b9ef11d452e1c8eca322ec35ac054a78195fe30bfbeacd1858e67092cc96248f724cab0c9d4b795b0bcdd8e75a3c678bce8ba9787973f47f33439
6
+ metadata.gz: dddf4370cdff6041fc2a0ceb338aa387ddcca76b65e135838cd0c3a1bfcf0bd0fcae37ea7504052b89b505b8c5478fb6d11e260e2349e25131b01853d42eae2d
7
+ data.tar.gz: 8d2f7087f39d05a4e8ccf72e675a07a38d9f371bdcfef33d96589a085f13abef53feb6986668fb12c75886ff9db301c0b7753b36e72177ef33944f08ce347734
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ 2023-08-16 - 1.5.0
2
+
3
+ - FEATURE: add to_sql for easy conversion of builder to sql
4
+ - FEATURE: improve active record compat
5
+ - FEATURE: change builder to use gsub vs sub, which allows repeat clauses
6
+
1
7
  2022-03-07 - 1.4.0
2
8
 
3
9
  - PERF: Optimize multiple use param in prepared postgres sql
data/README.md CHANGED
@@ -77,23 +77,34 @@ builder.query.each do |t|
77
77
  end
78
78
  ```
79
79
 
80
+ The same builder's statement may occur multiple times.
81
+
82
+ ```ruby
83
+ builder = conn.build('(/*select*/ from books) union (/*select*/ from movies)').select('title').query
84
+
85
+ # => (SELECT title from books) union (SELECT title from movies)
86
+ ```
87
+
80
88
  The builder predefined next _SQL Literals_
81
89
 
82
- | Method | SQL Literal |
83
- | ------ | ----------- |
84
- |`select` |`/*select*/`|
85
- |`where` |`/*where*/`|
86
- |`where2` |`/*where2*/`|
87
- |`join` |`/*join*/`|
88
- |`left_join` |`/*left_join*/`|
89
- |`group_by` |`/*group_by*/`|
90
- |`order_by` |`/*order_by*/`|
91
- |`limit` |`/*limit*/`|
92
- |`offset` |`/*offset*/`|
93
- |`set` |`/*set*/`|
90
+ | Method | SQL Literal |
91
+ |-------------|--------------|
92
+ | `select` | `/*select*/` |
93
+ | `count` | `/*select*/` |
94
+ | `where` | `/*where*/` |
95
+ | `where_or` | `/*where*/` |
96
+ | `where2` | `/*where2*/` |
97
+ | `where2_or` | `/*where2*/` |
98
+ | `join` | `/*join*/` |
99
+ | `left_join` | `/*left_join*/` |
100
+ | `group_by` | `/*group_by*/` |
101
+ | `order_by` | `/*order_by*/` |
102
+ | `limit` | `/*limit*/` |
103
+ | `offset` | `/*offset*/` |
104
+ | `set` | `/*set*/` |
94
105
 
95
106
  ### Custom SQL Literals
96
- Use `sql_literal` for injecting custom sql into Builder
107
+ Use `sql_literal` to inject SQL into Builder from `String`, `Builder`, `ActiveRecord::Relation`, or any object that implements `to_sql` method.
97
108
 
98
109
  ```ruby
99
110
  user_builder = conn
@@ -101,9 +112,9 @@ user_builder = conn
101
112
  .where('type = ?', input_type)
102
113
  .group_by("date_trunc('day', created_at)")
103
114
 
104
- guest_builder = conn
105
- .build("select date_trunc('day', created_at) day, count(*) from guest_topics /*where*/")
106
- .where('state = ?', input_state)
115
+ guest_relation = GuestTopic
116
+ .select("date_trunc('day', created_at) day, count(*)")
117
+ .where(state: input_state)
107
118
  .group_by("date_trunc('day', created_at)")
108
119
 
109
120
  conn
@@ -113,8 +124,8 @@ conn
113
124
  from u
114
125
  /*custom_join*/
115
126
  SQL
116
- .sql_literal(user: user_builder, guest: guest_builder) # builder
117
- .sql_literal(custom_join: "#{input_cond ? 'FULL' : 'LEFT'} JOIN g on g.day = u.day") # or string
127
+ .sql_literal(user: user_builder, guest: guest_relation) # Builder and ActiveRecord::Relation
128
+ .sql_literal(custom_join: "#{input_cond ? 'FULL' : 'LEFT'} JOIN g on g.day = u.day") # or String
118
129
  .query
119
130
  ```
120
131
 
@@ -11,8 +11,13 @@ class MiniSql::Builder
11
11
  @is_prepared = false
12
12
  end
13
13
 
14
+ def initialize_copy(_original_builder)
15
+ @args = @args.transform_values { |v| v.dup }
16
+ @sections = @sections.transform_values { |v| v.dup }
17
+ end
18
+
14
19
  literals1 =
15
- [:set, :where2, :where, :order_by, :left_join, :join, :select, :group_by].each do |k|
20
+ [:set, :where2, :where2_or, :where, :where_or, :order_by, :left_join, :join, :select, :group_by].each do |k|
16
21
  define_method k do |sql_part, *args|
17
22
  if Hash === args[0]
18
23
  @args.merge!(args[0])
@@ -48,7 +53,7 @@ class MiniSql::Builder
48
53
  if PREDEFINED_SQL_LITERALS.include?(name)
49
54
  raise "/*#{name}*/ is predefined, use method `.#{name}` instead `sql_literal`"
50
55
  end
51
- @sections[name] = part_sql.is_a?(::MiniSql::Builder) ? part_sql.to_sql : part_sql
56
+ @sections[name] = part_sql.respond_to?(:to_sql) ? part_sql.to_sql : part_sql
52
57
  end
53
58
  self
54
59
  end
@@ -75,6 +80,10 @@ class MiniSql::Builder
75
80
  @connection.param_encoder.encode(parametrized_sql, union_parameters(hash_args))
76
81
  end
77
82
 
83
+ def count(field = '*')
84
+ dup.select("count(#{field})").query_single.first
85
+ end
86
+
78
87
  private def connection_switcher
79
88
  if @is_prepared
80
89
  @connection.prepared
@@ -83,35 +92,43 @@ class MiniSql::Builder
83
92
  end
84
93
  end
85
94
 
95
+ WHERE_SECTIONS = [%i[where where_or], %i[where2 where2_or]]
86
96
  private def parametrized_sql
87
97
  sql = @sql.dup
88
98
 
89
- @sections.each do |k, v|
90
- joined = nil
91
- case k
92
- when :select
93
- joined = (+"SELECT ") << v.join(" , ")
94
- when :where, :where2
95
- joined = (+"WHERE ") << v.map { |c| (+"(") << c << ")" }.join(" AND ")
96
- when :join
97
- joined = v.map { |item| (+"JOIN ") << item }.join("\n")
98
- when :left_join
99
- joined = v.map { |item| (+"LEFT JOIN ") << item }.join("\n")
100
- when :limit
101
- joined = (+"LIMIT :mq_auto_limit")
102
- when :offset
103
- joined = (+"OFFSET :mq_auto_offset")
104
- when :order_by
105
- joined = (+"ORDER BY ") << v.join(" , ")
106
- when :group_by
107
- joined = (+"GROUP BY ") << v.join(" , ")
108
- when :set
109
- joined = (+"SET ") << v.join(" , ")
110
- else # for sql_literal
111
- joined = v
99
+ WHERE_SECTIONS.each do |section_and, section_or|
100
+ if (or_values = @sections.delete(section_or))
101
+ @sections[section_and] ||= []
102
+ @sections[section_and] << or_values.map { |c| "(#{c})" }.join(" OR ")
112
103
  end
104
+ end
105
+
106
+ @sections.each do |k, v|
107
+ joined =
108
+ case k
109
+ when :select
110
+ "SELECT #{v.join(" , ")}"
111
+ when :where, :where2
112
+ "WHERE #{v.map { |c| "(#{c})" }.join(" AND ")}"
113
+ when :join
114
+ v.map { |item| "JOIN #{item}" }.join("\n")
115
+ when :left_join
116
+ v.map { |item| "LEFT JOIN #{item}" }.join("\n")
117
+ when :limit
118
+ "LIMIT :mq_auto_limit"
119
+ when :offset
120
+ "OFFSET :mq_auto_offset"
121
+ when :order_by
122
+ "ORDER BY #{v.join(" , ")}"
123
+ when :group_by
124
+ "GROUP BY #{v.join(" , ")}"
125
+ when :set
126
+ "SET #{v.join(" , ")}"
127
+ else # for sql_literal
128
+ v
129
+ end
113
130
 
114
- unless sql.sub!("/*#{k}*/", joined)
131
+ unless sql.gsub!("/*#{k}*/", joined)
115
132
  raise "The section for the /*#{k}*/ clause was not found!"
116
133
  end
117
134
  end
@@ -57,6 +57,14 @@ module MiniSql
57
57
  Builder.new(self, sql)
58
58
  end
59
59
 
60
+ def to_sql(sql, *params)
61
+ if params.empty?
62
+ sql
63
+ else
64
+ param_encoder.encode(sql, *params)
65
+ end
66
+ end
67
+
60
68
  def escape_string(str)
61
69
  raise NotImplementedError, "must be implemented by child connection"
62
70
  end
@@ -51,18 +51,11 @@ module MiniSql
51
51
  raw_connection.escape(str)
52
52
  end
53
53
 
54
- def build(sql)
55
- Builder.new(self, sql)
56
- end
57
-
58
54
  private
59
55
 
60
56
  def run(sql, as, params)
61
- if params && params.length > 0
62
- sql = param_encoder.encode(sql, *params)
63
- end
64
57
  raw_connection.query(
65
- sql,
58
+ to_sql(sql, *params),
66
59
  as: as,
67
60
  database_timezone: :utc,
68
61
  application_timezone: :utc,
@@ -192,10 +192,6 @@ module MiniSql
192
192
  result.clear if result
193
193
  end
194
194
 
195
- def build(sql)
196
- Builder.new(self, sql)
197
- end
198
-
199
195
  def escape_string(str)
200
196
  raw_connection.escape_string(str)
201
197
  end
@@ -203,10 +199,7 @@ module MiniSql
203
199
  private
204
200
 
205
201
  def run(sql, params)
206
- if params && params.length > 0
207
- sql = param_encoder.encode(sql, *params)
208
- end
209
- raw_connection.async_exec(sql)
202
+ raw_connection.async_exec(to_sql(sql, *params))
210
203
  end
211
204
 
212
205
  end
@@ -78,10 +78,6 @@ module MiniSql
78
78
  run(sql, params).to_a
79
79
  end
80
80
 
81
- def build(sql)
82
- Builder.new(self, sql)
83
- end
84
-
85
81
  def escape_string(str)
86
82
  raw_connection.escape_string(str)
87
83
  end
@@ -89,12 +85,9 @@ module MiniSql
89
85
  private
90
86
 
91
87
  def run(sql, params)
92
- if params && params.length > 0
93
- sql = param_encoder.encode(sql, *params)
94
- end
95
88
  conn = raw_connection
96
89
  conn.typemap = self.class.typemap
97
- conn.execute(sql)
90
+ conn.execute(to_sql(sql, *params))
98
91
  ensure
99
92
  # Force unsetting of typemap since we don't want mixed AR usage to continue to use these extra converters.
100
93
  conn.typemap = nil
@@ -21,12 +21,12 @@ module MiniSql
21
21
 
22
22
  def query_single(sql, *params)
23
23
  # a bit lazy can be optimized
24
- run(sql, *params).flatten!
24
+ run(sql, params).flatten!
25
25
  end
26
26
 
27
27
  def query_hash(sql, *params)
28
28
  r = []
29
- run(sql, *params) do |set|
29
+ run(sql, params) do |set|
30
30
  set.each_hash do |h|
31
31
  r << h
32
32
  end
@@ -35,14 +35,14 @@ module MiniSql
35
35
  end
36
36
 
37
37
  def query_array(sql, *params)
38
- run(sql, *params)
38
+ run(sql, params)
39
39
  end
40
40
 
41
41
  def exec(sql, *params)
42
42
 
43
43
  start = raw_connection.total_changes
44
44
 
45
- r = run(sql, *params)
45
+ r = run(sql, params)
46
46
  # this is not safe for multithreading, also for DELETE from TABLE will return
47
47
  # incorrect data
48
48
  if r.length > 0
@@ -53,13 +53,13 @@ module MiniSql
53
53
  end
54
54
 
55
55
  def query(sql, *params)
56
- run(sql, *params) do |set|
56
+ run(sql, params) do |set|
57
57
  deserializer_cache.materialize(set)
58
58
  end
59
59
  end
60
60
 
61
61
  def query_decorator(decorator, sql, *params)
62
- run(sql, *params) do |set|
62
+ run(sql, params) do |set|
63
63
  deserializer_cache.materialize(set, decorator)
64
64
  end
65
65
  end
@@ -70,10 +70,8 @@ module MiniSql
70
70
 
71
71
  private
72
72
 
73
- def run(sql, *params)
74
- if params && params.length > 0
75
- sql = param_encoder.encode(sql, *params)
76
- end
73
+ def run(sql, params)
74
+ sql = to_sql(sql, *params)
77
75
  if block_given?
78
76
  stmt = SQLite3::Statement.new(raw_connection, sql)
79
77
  result = yield stmt.execute
@@ -28,7 +28,7 @@ module MiniSql
28
28
  end
29
29
 
30
30
  private def run(sql, params)
31
- prepared_sql, binds, _bind_names = @param_binder.bind(sql, params)
31
+ prepared_sql, binds, _bind_names = @param_binder.bind(sql, *params)
32
32
  statement = @prepared_cache.prepare_statement(prepared_sql)
33
33
  statement.bind_params(binds)
34
34
  if block_given?
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module MiniSql
3
- VERSION = "1.4.0"
3
+ VERSION = "1.5.0"
4
4
  end
data/mini_sql.gemspec CHANGED
@@ -47,7 +47,7 @@ Gem::Specification.new do |spec|
47
47
  else
48
48
  spec.add_development_dependency "pg", "> 1"
49
49
  spec.add_development_dependency "mysql2"
50
- spec.add_development_dependency "sqlite3", "~> 1.3"
50
+ spec.add_development_dependency "sqlite3", "~> 1.4.4"
51
51
  spec.add_development_dependency "activerecord", "~> 7.0.0"
52
52
  end
53
53
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-07 00:00:00.000000000 Z
11
+ date: 2023-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -170,14 +170,14 @@ dependencies:
170
170
  requirements:
171
171
  - - "~>"
172
172
  - !ruby/object:Gem::Version
173
- version: '1.3'
173
+ version: 1.4.4
174
174
  type: :development
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
- version: '1.3'
180
+ version: 1.4.4
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: activerecord
183
183
  requirement: !ruby/object:Gem::Requirement