terrazine 0.0.2 → 0.0.3

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.
@@ -1,251 +1,70 @@
1
+ require_relative 'builders/operators'
2
+ require_relative 'builders/predicates'
3
+ require_relative 'builders/expressions'
4
+ require_relative 'builders/clauses'
5
+ require_relative 'builders/params'
6
+
1
7
  module Terrazine
2
- # build structures in to sql string
8
+ # builds structures in to sql string
9
+ # TODO: SPLIT!!! But how-_-
10
+ # Operators(sql_functions), Predicates, Clauses(select, from...), Expressions(columns, tables), Params...
11
+ # they are mixed... everything can contain everything and they must communicate with each other.
12
+ # And how it can be splitted?
3
13
  class Builder
4
- attr_accessor :sql, :constructor
14
+ # https://6ftdan.com/allyourdev/2015/05/02/private-module-methods-in-ruby/
15
+ # TODO: all methods private except get_sql, get_partial_sql ?
5
16
 
6
- def initialize(constructor)
7
- @constructor = constructor
17
+ def initialize
8
18
  @params = []
9
19
  end
10
20
 
11
- # TODO: update, delete, insert.....
21
+ # get complete sql structure for constructor.
22
+ def get_sql(structure, options)
23
+ # get_partial_sql structure, key: 'sql'
24
+ wrap_result send("build_#{options[:key] || 'sql'}", structure)
25
+ end
26
+
27
+ # def get_partial_sql(structure, options)
28
+ # wrap_result send("build_#{options[:key]}", structure)
29
+ # end
30
+
31
+ private
32
+
33
+ # TODO: update, delete, insert, group.....
12
34
  def build_sql(structure)
13
35
  structure = structure.is_a?(Constructor) ? structure.structure : structure
14
36
  sql = ''
15
- sql += "WITH #{build_with(structure[:with])} " if structure[:with]
16
- # puts "build_sql, structure: #{structure}"
17
- [:union, :select, :insert, :update, :delete, :set, :from, :join, :where,
18
- :group, :order, :limit, :offset].each do |i|
37
+ [:with, :union, :select, :insert, :update, :delete, :set, :from,
38
+ :join, :where, :returning, :group, :order, :limit, :offset].each do |i|
19
39
  next unless structure[i]
20
- sql += send("build_#{i}".to_sym, structure[i])
40
+ sql += send("build_#{i}", structure[i], structure)
21
41
  end
22
42
  sql
23
43
  end
24
44
 
25
- # get complete sql structure for constructor.
26
- def get_sql(structure)
27
- sql = build_sql structure
28
- res = @params.count.positive? ? [sql, @params] : sql
29
- @params = []
30
- res
31
- end
32
-
33
- def build_with(structure)
34
- if structure.second.is_a? Hash
35
- "#{structure.first} AS (#{build_sql(structure.last)})"
45
+ def method_missing(name, *args)
46
+ /(?<type>[^_]+)_(?<action>\w+)/ =~ name
47
+ # arguments = [action]
48
+ if type && respond_to?("#{type}_missing", true)
49
+ send "#{type}_missing", action, *args
36
50
  else
37
- structure.map { |v| build_with(v) }.join ', '
51
+ super
38
52
  end
39
53
  end
40
54
 
41
- # def build_select_query(structure)
42
- # puts "build_select_query, structure: #{structure}"
43
- # sql += build_select(structure[:select], structure[:distinct]) if structure[:select]
44
- # [:from, :join, :where, :order, :limit, :offset].each do |i|
45
- # sql += send("build_#{i}", structure[i]) if structure[i]
46
- # end
55
+ # TODO
56
+ # def construct_as(field, name)
47
57
  # end
48
58
 
49
- def build_union(structure)
50
- structure.map { |i| build_sql(i) }.join ' UNION '
51
- end
52
-
53
- def build_distinct_select(distinct)
54
- case distinct
55
- when Array
56
- "DISTINCT ON(#{build_columns fields}) "
57
- when true
58
- 'DISTINCT '
59
- end
60
- end
61
-
62
- def build_select(structure, distinct = nil)
63
- # puts "build_select, structure #{structure}"
64
- "SELECT #{build_distinct_select distinct}#{build_columns structure} "
65
- end
66
-
67
- def build_tables(structure)
68
- case structure
69
- when Array
70
- if check_alias(structure.first) # VALUES function or ...?
71
- build_function(structure)
72
- # if it's a array with strings/values
73
- elsif structure.select { |i| i.is_a? Array }.empty? # array of table_name and alias
74
- structure.join ' '
75
- else # array of tables/values
76
- structure.map { |i| i.is_a?(Array) ? build_tables(i) : i }.join(', ')
77
- end
78
- when String, Symbol
79
- structure
80
- else
81
- raise "Undefined structure for FROM - #{structure}"
82
- end
83
- end
84
-
85
- def build_from(structure)
86
- "FROM #{build_tables(structure)} "
87
- end
88
-
89
- def conditions_constructor(structure, joiner = :and, level = nil)
90
- case structure
91
- when Array
92
- key = structure.first
93
- # AND, OR support
94
- if key.is_a? Symbol
95
- res = structure.drop(1).map { |i| conditions_constructor(i) }.join " #{key} ".upcase
96
- level ? res : "(#{res})"
97
- # Sub Queries support - ['rgl IN ?', {...}]
98
- elsif key =~ /\?/
99
- if [Hash, Constructor].include?(structure.second.class)
100
- key.sub(/\?/, "(#{build_sql(structure.second)})")
101
- else
102
- key.sub(/\?/, build_param(structure.second))
103
- end
104
- else
105
- res = structure.map { |i| conditions_constructor(i) }.join " #{joiner} ".upcase
106
- level ? res : "(#{res})"
107
- end
108
- when String
109
- structure
110
- end
111
- end
112
-
113
- # TODO? conditions like [:eq :name :Aeonax]
114
- def build_conditions(structure)
115
- conditions_constructor(structure, :and, true) + ' '
116
- end
117
-
118
- # TODO: -_-
119
- def build_join(structure)
120
- if structure.is_a? Array
121
- # TODO: hash is sux here -_- !!!!!!
122
- if structure.second.is_a? Hash
123
- name = build_tables structure.first # (name.is_a?(Array) ? name.join(' ') : name)
124
- v = structure.second
125
- "#{v[:option].to_s.upcase + ' ' if v[:option]}JOIN #{name} ON #{build_conditions v[:on]}"
126
- else
127
- structure.map { |i| build_join(i) }.join
128
- end
129
- else
130
- structure =~ /join/i ? structure : "JOIN #{structure} "
131
- end
132
- end
133
-
134
- def build_where(structure)
135
- "WHERE #{build_conditions(structure)} "
136
- end
137
-
138
- # TODO!
139
- def build_order(structure)
140
- "ORDER BY #{structure} "
141
- end
142
-
143
- def build_limit(limit)
144
- "LIMIT #{limit || 8} "
145
- end
146
-
147
- def build_offset(offset)
148
- "OFFSET #{offset || 0} "
149
- end
150
-
151
- private
152
-
153
- def build_param(value)
154
- # no need for injections check - pg gem will check it
155
- @params << value
156
- "$#{@params.count}"
157
- end
158
-
159
59
  # all functions and column aliases begins from _
160
60
  def check_alias(val)
161
61
  val.to_s =~ /^_/
162
62
  end
163
63
 
164
- def iterate_hash(data)
64
+ def iterate_hash(data, join = true)
165
65
  iterations = []
166
66
  data.each { |k, v| iterations << yield(k, v) }
167
- iterations.join ', '
168
- end
169
-
170
- def build_as(field, name)
171
- "#{field} AS #{name.to_s.sub(/^_/, '')}" # update ruby for delete_prefix? =)
172
- end
173
-
174
- def build_columns(structure, prefix = nil)
175
- case structure
176
- when Array
177
- # SQL function - in format: "_#{fn}"
178
- if check_alias(structure.first)
179
- build_function structure, prefix
180
- else
181
- structure.map { |i| build_columns i, prefix }.join ', '
182
- end
183
- when Hash
184
- # sub_query
185
- if structure[:select]
186
- "(#{build_sql(structure)})"
187
- # colum OR table alias
188
- else
189
- iterate_hash(structure) do |k, v|
190
- if check_alias(k)
191
- build_as(build_columns(v, prefix), k)
192
- else
193
- build_columns(v, k.to_s)
194
- end
195
- end
196
- end
197
- when Symbol, String
198
- structure = structure.to_s
199
- if prefix && structure !~ /, |\./
200
- "#{prefix}.#{structure}"
201
- else
202
- structure
203
- end
204
- when Constructor
205
- "(#{build_sql structure.structure})"
206
- when true # choose everything -_-
207
- build_columns('*', prefix)
208
- else # TODO: values from value passing here... -_-
209
- structure
210
- # raise "Undefined class: #{structure.class} of #{structure}" # TODO: ERRORS class
211
- end
212
- end
213
-
214
- # TODO!!!!!!! Relocate in class FunctionsBuilder? and send function name in it.
215
- def build_function(structure, prefix = nil)
216
- function = structure.first.to_s.sub(/^_/, '')
217
- arguments = structure.drop(1)
218
- case function.to_sym
219
- when :param
220
- build_param arguments.first
221
- when :count # TODO? alias support on this lvl
222
- if arguments.count > 1
223
- arguments.map { |i| "COUNT(#{build_columns(i, prefix)})" }.join ','
224
- else
225
- "COUNT(#{build_columns(arguments.first, prefix)})"
226
- end
227
- when :nullif
228
- # TODO? querry for value
229
- "NULLIF(#{build_columns(arguments.first, prefix)}, #{arguments[1]})"
230
- when :array # TODO? build_columns support
231
- if [Hash, Constructor].include?(arguments.first.class)
232
- "ARRAY(#{build_sql arguments.first})"
233
- else # TODO? condition and error case
234
- "ARRAY[#{arguments.join ', '}]"
235
- end
236
- when :avg
237
- "AVG(#{build_columns(arguments.first, prefix)})"
238
- when :values
239
- "(VALUES(#{build_columns arguments.first, prefix})) AS #{structure[2]} (#{build_columns arguments.last})"
240
- when :case
241
- else_val = "ELSE #{arguments.pop} " unless arguments.last.is_a? Array
242
- conditions = arguments.map { |i| "WHEN #{i.first} THEN #{i.last}" }.join ' '
243
- "CASE #{conditions} #{else_val}END"
244
- when :coalesce
245
- "COALESCE(#{build_columns(arguments, prefix)})"
246
- else
247
- raise "Unknown function #{function}" # TODO: errors-_-
248
- end
67
+ join ? iterations.join(', ') : iterations
249
68
  end
250
69
  end
251
70
  end
@@ -0,0 +1,145 @@
1
+ module Terrazine
2
+ class Builder
3
+ # it use Predicate, expressions
4
+
5
+ private
6
+
7
+ # TODO: :with_recursive
8
+ def build_with(structure, _)
9
+ "WITH #{construct_with(structure)} "
10
+ end
11
+
12
+ def build_union(structure, _)
13
+ structure.map { |i| build_sql(i) }.join ' UNION '
14
+ end
15
+
16
+ def build_select(structure, common_structure)
17
+ distinct = construct_distinct common_structure[:distinct]
18
+ "SELECT #{distinct}#{build_columns structure} "
19
+ end
20
+
21
+ def build_from(structure, _)
22
+ "FROM #{build_tables(structure)} "
23
+ end
24
+
25
+ # TODO: -_-
26
+ def build_join(structure, _)
27
+ if structure.is_a? Array
28
+ # TODO: hash is sux here -_- !!!!!!
29
+ if structure.second.is_a? Hash
30
+ name = build_tables structure.first # (name.is_a?(Array) ? name.join(' ') : name)
31
+ v = structure.second
32
+ "#{v[:option].to_s.upcase + ' ' if v[:option]}JOIN #{name} ON #{build_predicates v[:on]} "
33
+ else
34
+ structure.map { |i| build_join(i, nil) }.join
35
+ end
36
+ else
37
+ structure =~ /join/i ? structure : "JOIN #{structure} "
38
+ end
39
+ end
40
+
41
+ # TODO!
42
+ def build_update(structure, _)
43
+ "UPDATE #{construct_update structure} "
44
+ end
45
+
46
+ def build_returning(structure, _)
47
+ "RETURNING #{build_columns structure}"
48
+ end
49
+
50
+ def build_where(structure, _)
51
+ "WHERE #{build_predicates(structure)} "
52
+ end
53
+
54
+ # TODO!
55
+ def build_order(structure, _)
56
+ "ORDER BY #{construct_order structure} "
57
+ end
58
+
59
+ def build_limit(limit, _)
60
+ "LIMIT #{limit || 8} "
61
+ end
62
+
63
+ def build_offset(offset, _)
64
+ "OFFSET #{offset || 0} "
65
+ end
66
+
67
+ def construct_with(structure)
68
+ case structure
69
+ when Array
70
+ if structure.second.is_a? Hash
71
+ "#{structure.first} AS (#{build_sql(structure.last)})"
72
+ else
73
+ structure.map { |v| construct_with(v) }.join ', '
74
+ end
75
+ when Hash
76
+ iterate_hash(structure) { |k, v| "#{k} AS (#{build_sql v})" }
77
+ else
78
+ raise
79
+ end
80
+ end
81
+
82
+ def construct_distinct(structure)
83
+ return unless structure
84
+ if structure == true
85
+ 'DISTINCT '
86
+ else
87
+ "DISTINCT ON(#{build_columns structure}) "
88
+ end
89
+ end
90
+
91
+ def construct_update(structure)
92
+ case structure
93
+ when Array
94
+ table = build_tables structure.first
95
+ "#{table} SET #{construct_set structure.last}"
96
+ when String
97
+ structure
98
+ else
99
+ raise "Undefined structure for `UPDATE`: #{structure}"
100
+ end
101
+ end
102
+
103
+ # TODO: (..., ...) = (..., ...)
104
+ def construct_set(structure)
105
+ case structure
106
+ when Hash
107
+ iterate_hash(structure) { |k, v| "#{build_columns k} = #{build_columns v}" }
108
+ when String
109
+ structure
110
+ else
111
+ raise "Undefined structure for `UPDATE`: #{structure}"
112
+ end
113
+ end
114
+
115
+ # { name: :asc, email: [:desc, :last] }
116
+ # [:name, :email, { phone: :last }]
117
+ def construct_order(structure)
118
+ case structure
119
+ when Array # function or values for order
120
+ if check_alias structure.first
121
+ build_operator structure
122
+ else
123
+ structure.map { |i| construct_order i }.join ', '
124
+ end
125
+ when Hash
126
+ iterate_hash(structure) { |k, v| "#{construct_order k} #{construct_order_options v}" }
127
+ else
128
+ structure
129
+ end
130
+ end
131
+
132
+ def construct_order_options(option)
133
+ case option
134
+ when Array
135
+ option.sort.map { |i| construct_order_options i }.join ' '
136
+ when :last, :first
137
+ "nulls #{option}".upcase
138
+ when :asc, :desc
139
+ option.to_s.upcase
140
+ else
141
+ "USING#{option}"
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,72 @@
1
+ module Terrazine
2
+ class Builder
3
+
4
+ # doesn't use Predicates
5
+ # use Operators, Expressions
6
+
7
+ private
8
+
9
+ # TODO: split
10
+ def build_tables(structure)
11
+ case structure
12
+ when Array
13
+ if check_alias(structure.first) # VALUES function or ...?
14
+ build_operator(structure)
15
+ # if it's a array with strings/values || array of tables/values
16
+ else
17
+ joiner = structure.select { |i| i.is_a? Array }.empty? ? ' ' : ', '
18
+ structure.map { |i| build_tables i }.join joiner
19
+ end
20
+ when Hash
21
+ "(#{build_sql structure})"
22
+ when String, Symbol
23
+ structure
24
+ else
25
+ raise "Undefined structure for FROM - #{structure}"
26
+ end
27
+ end
28
+
29
+ # TODO: split
30
+ def build_columns(structure, prefix = nil)
31
+ case structure
32
+ when Array
33
+ # SQL function - in format: "_#{fn}"
34
+ if check_alias(structure.first)
35
+ build_operator structure, prefix
36
+ else
37
+ structure.map { |i| build_columns i, prefix }.join ', '
38
+ end
39
+ when Hash
40
+ # sub_query
41
+ if structure[:select]
42
+ "(#{build_sql(structure)})"
43
+ # colum OR table alias
44
+ else
45
+ iterate_hash(structure) do |k, v|
46
+ if check_alias(k)
47
+ # update ruby for delete_prefix? =)
48
+ "#{build_columns(v, prefix)} AS #{k.to_s.sub(/^_/, '')}"
49
+ # construct_as(build_columns(v, prefix), k)
50
+ else
51
+ build_columns(v, k.to_s)
52
+ end
53
+ end
54
+ end
55
+ when Symbol, String, Integer
56
+ structure = structure.to_s
57
+ if prefix && structure !~ /, |\.|\(/
58
+ "#{prefix}.#{structure}"
59
+ else
60
+ structure
61
+ end
62
+ when Constructor
63
+ "(#{build_sql structure.structure})"
64
+ when true # choose everything -_-
65
+ build_columns('*', prefix)
66
+ else # TODO: values from value passing here... -_-
67
+ structure
68
+ # raise "Undefined class: #{structure.class} of #{structure}" # TODO: ERRORS class
69
+ end
70
+ end
71
+ end
72
+ end