baza 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,499 @@
1
+ class Baza::ModelHandler
2
+ #This method helps build SQL from Objects-instances list-method. It should not be called directly but only through Objects.list.
3
+ def sqlhelper(list_args, args_def)
4
+ args = args_def
5
+
6
+ if args[:db]
7
+ db = args[:db]
8
+ else
9
+ db = @args[:db]
10
+ end
11
+
12
+ if args[:table]
13
+ table_def = "`#{db.esc_table(args[:table])}`."
14
+ else
15
+ table_def = ""
16
+ end
17
+
18
+ sql_joins = ""
19
+ sql_where = ""
20
+ sql_order = ""
21
+ sql_limit = ""
22
+ sql_groupby = ""
23
+
24
+ do_joins = {}
25
+
26
+ limit_from = nil
27
+ limit_to = nil
28
+
29
+ if list_args.key?("orderby")
30
+ orders = []
31
+ orderstr = list_args["orderby"]
32
+ list_args["orderby"] = [list_args["orderby"]] if list_args["orderby"].is_a?(Hash)
33
+
34
+ if list_args["orderby"].is_a?(String)
35
+ found = false
36
+ found = true if args[:cols].key?(orderstr)
37
+
38
+ if found
39
+ sql_order << " ORDER BY "
40
+ ordermode = " ASC"
41
+ if list_args.key?("ordermode")
42
+ if list_args["ordermode"] == "desc"
43
+ ordermode = " DESC"
44
+ elsif list_args["ordermode"] == "asc"
45
+ ordermode = " ASC"
46
+ raise "Unknown ordermode: #{list_args["ordermode"]}"
47
+ end
48
+
49
+ list_args.delete("ordermode")
50
+ end
51
+
52
+ sql_order << "#{table_def}`#{db.esc_col(list_args["orderby"])}`#{ordermode}"
53
+ list_args.delete("orderby")
54
+ end
55
+ elsif list_args["orderby"].is_a?(Array)
56
+ sql_order << " ORDER BY "
57
+
58
+ list_args["orderby"].each do |val|
59
+ ordermode = nil
60
+ orderstr = nil
61
+ found = false
62
+
63
+ if val.is_a?(Array)
64
+ if val[1] == "asc"
65
+ ordermode = " ASC"
66
+ elsif val[1] == "desc"
67
+ ordermode = " DESC"
68
+ end
69
+
70
+ if val[0].is_a?(Array)
71
+ if args[:joined_tables]
72
+ args[:joined_tables].each do |table_name, table_data|
73
+ next if table_name.to_s != val[0][0].to_s
74
+ do_joins[table_name] = true
75
+ orders << "`#{db.esc_table(table_name)}`.`#{db.esc_col(val[0][1])}`#{ordermode}"
76
+ found = true
77
+ break
78
+ end
79
+ end
80
+
81
+ raise "Could not find joined table for ordering: '#{val[0][0]}'." if !found
82
+ else
83
+ orderstr = val[0]
84
+ end
85
+ elsif val.is_a?(String)
86
+ orderstr = val
87
+ ordermode = " ASC"
88
+ elsif val.is_a?(Hash) and val[:type] == :sql
89
+ orders << val[:sql]
90
+ found = true
91
+ elsif val.is_a?(Hash) and val[:type] == :case
92
+ caseorder = " CASE"
93
+
94
+ val[:case].each do |key, caseval|
95
+ col = key.first
96
+ isval = key.last
97
+ col_str = nil
98
+
99
+ if col.is_a?(Array)
100
+ raise "No joined tables for '#{args[:table]}'." if !args[:joined_tables]
101
+
102
+ found = false
103
+ args[:joined_tables].each do |table_name, table_data|
104
+ if table_name == col.first
105
+ do_joins[table_name] = true
106
+ col_str = "`#{db.esc_table(table_name)}`.`#{db.esc_col(col.last)}`"
107
+ found = true
108
+ break
109
+ end
110
+ end
111
+
112
+ raise "No such joined table on '#{args[:table]}': '#{col.first}' (#{col.first.class.name}) with the following joined table:\n#{Php4r.print_r(args[:joined_tables], true)}" if !found
113
+ elsif col.is_a?(String) or col.is_a?(Symbol)
114
+ col_str = "#{table_def}`#{col}`"
115
+ found = true
116
+ else
117
+ raise "Unknown type for case-ordering: '#{col.class.name}'."
118
+ end
119
+
120
+ raise "'colstr' was not set." if !col_str
121
+ caseorder << " WHEN #{col_str} = '#{db.esc(isval)}' THEN '#{db.esc(caseval)}'"
122
+ end
123
+
124
+ if val[:else]
125
+ caseorder << " ELSE '#{db.esc(val[:else])}'"
126
+ end
127
+
128
+ caseorder << " END"
129
+ orders << caseorder
130
+ elsif val.is_a?(Hash)
131
+ raise "No joined tables." if !args.key?(:joined_tables)
132
+
133
+ if val[:mode] == "asc"
134
+ ordermode = " ASC"
135
+ elsif val[:mode] == "desc"
136
+ ordermode = " DESC"
137
+ end
138
+
139
+ if args[:joined_tables]
140
+ args[:joined_tables].each do |table_name, table_data|
141
+ if table_data[:parent_table]
142
+ table_name_real = table_name
143
+ elsif table_data[:datarow]
144
+ table_name_real = self.datarow_from_datarow_argument(table_data[:datarow]).classname
145
+ else
146
+ table_name_real = @args[:module].const_get(table_name).classname
147
+ end
148
+
149
+ if table_name.to_s == val[:table].to_s
150
+ do_joins[table_name] = true
151
+
152
+ if val[:sql]
153
+ orders << val[:sql]
154
+ elsif val[:col]
155
+ orders << "`#{db.esc_table(table_name_real)}`.`#{db.esc_col(val[:col])}`#{ordermode}"
156
+ else
157
+ raise "Couldnt figure out how to order based on keys: '#{val.keys.sort}'."
158
+ end
159
+
160
+ found = true
161
+ break
162
+ end
163
+ end
164
+ end
165
+ else
166
+ raise "Unknown object: #{val.class.name}"
167
+ end
168
+
169
+ found = true if args[:cols].key?(orderstr)
170
+
171
+ if !found
172
+ raise "Column not found for ordering: #{orderstr}."
173
+ end
174
+
175
+ orders << "#{table_def}`#{db.esc_col(orderstr)}`#{ordermode}" if orderstr
176
+ end
177
+
178
+ sql_order << orders.join(", ")
179
+ list_args.delete("orderby")
180
+ else
181
+ raise "Unknown orderby object: #{list_args["orderby"].class.name}."
182
+ end
183
+ end
184
+
185
+ list_args.each do |realkey, val|
186
+ found = false
187
+
188
+ if realkey.is_a?(Array)
189
+ if !args[:joins_skip]
190
+ datarow_obj = self.datarow_obj_from_args(args_def, list_args, realkey[0])
191
+ args = datarow_obj.columns_sqlhelper_args
192
+ raise "Couldnt get arguments from SQLHelper." if !args
193
+ else
194
+ datarow_obj = @args[:module].const_get(realkey[0])
195
+ args = args_def
196
+ end
197
+
198
+ table_sym = realkey[0].to_sym
199
+ do_joins[table_sym] = true
200
+ list_table_name_real = table_sym
201
+ table = "`#{db.esc_table(list_table_name_real)}`."
202
+ key = realkey[1]
203
+ else
204
+ table = table_def
205
+ args = args_def
206
+ key = realkey
207
+ end
208
+
209
+ if args.key?(:cols_bools) and args[:cols_bools].index(key) != nil
210
+ val_s = val.to_s
211
+
212
+ if val_s == "1" or val_s == "true"
213
+ realval = "1"
214
+ elsif val_s == "0" or val_s == "false"
215
+ realval = "0"
216
+ else
217
+ raise "Could not make real value out of class: #{val.class.name} => #{val}."
218
+ end
219
+
220
+ sql_where << " AND #{table}`#{db.esc_col(key)}` = '#{db.esc(realval)}'"
221
+ found = true
222
+ elsif args[:cols].key?(key)
223
+ if val.is_a?(Array)
224
+ if val.empty? and db.opts[:type].to_s == "mysql"
225
+ sql_where << " AND false"
226
+ else
227
+ escape_sql = Knj::ArrayExt.join(
228
+ :arr => val,
229
+ :callback => proc{|value|
230
+ db.escape(value)
231
+ },
232
+ :sep => ",",
233
+ :surr => "'"
234
+ )
235
+ sql_where << " AND #{table}`#{db.esc_col(key)}` IN (#{escape_sql})"
236
+ end
237
+ elsif val.is_a?(Hash) and val[:type].to_sym == :col
238
+ raise "No table was given for join: '#{val}', key: '#{key}' on table #{table}." if !val.key?(:table)
239
+ do_joins[val[:table].to_sym] = true
240
+ sql_where << " AND #{table}`#{db.esc_col(key)}` = `#{db.esc_table(val[:table])}`.`#{db.esc_col(val[:name])}`"
241
+ elsif val.is_a?(Hash) and val[:type] == :sqlval and val[:val] == :null
242
+ sql_where << " AND #{table}`#{db.esc_col(key)}` IS NULL"
243
+ elsif val.is_a?(Proc)
244
+ call_args = Knj::Hash_methods.new(:ob => self, :db => db)
245
+ sql_where << " AND #{table}`#{db.esc_col(key)}` = '#{db.esc(val.call(call_args))}'"
246
+ else
247
+ sql_where << " AND #{table}`#{db.esc_col(key)}` = '#{db.esc(val)}'"
248
+ end
249
+
250
+ found = true
251
+ elsif key.to_s == "limit_from"
252
+ limit_from = val.to_i
253
+ found = true
254
+ elsif key.to_s == "limit_to"
255
+ limit_to = val.to_i
256
+ found = true
257
+ elsif key.to_s == "limit"
258
+ limit_from = 0
259
+ limit_to = val.to_i
260
+ found = true
261
+ elsif args.key?(:cols_dbrows) and args[:cols_dbrows].index("#{key.to_s}_id") != nil
262
+ if val == false
263
+ sql_where << " AND #{table}`#{db.esc_col(key.to_s + "_id")}` = '0'"
264
+ elsif val.is_a?(Array)
265
+ if val.empty?
266
+ sql_where << " AND false"
267
+ else
268
+ sql_where << " AND #{table}`#{db.esc_col("#{key}_id")}` IN (#{Knj::ArrayExt.join(:arr => val, :sep => ",", :surr => "'", :callback => proc{|obj| obj.id.sql})})"
269
+ end
270
+ else
271
+ sql_where << " AND #{table}`#{db.esc_col(key.to_s + "_id")}` = '#{db.esc(val.id)}'"
272
+ end
273
+
274
+ found = true
275
+ elsif match = key.match(/^([A-z_\d]+)_(search|has)$/) and args[:cols].key?(match[1]) != nil
276
+ if match[2] == "search"
277
+ Knj::Strings.searchstring(val).each do |str|
278
+ sql_where << " AND #{table}`#{db.esc_col(match[1])}` LIKE '%#{db.esc(str)}%'"
279
+ end
280
+ elsif match[2] == "has"
281
+ if val
282
+ sql_where << " AND #{table}`#{db.esc_col(match[1])}` != ''"
283
+ else
284
+ sql_where << " AND #{table}`#{db.esc_col(match[1])}` = ''"
285
+ end
286
+ end
287
+
288
+ found = true
289
+ elsif match = key.match(/^([A-z_\d]+)_(not|lower)$/) and args[:cols].key?(match[1])
290
+ if match[2] == "not"
291
+ if val.is_a?(Array)
292
+ if val.empty?
293
+ #ignore.
294
+ else
295
+ escape_sql = Knj::ArrayExt.join(
296
+ :arr => val,
297
+ :callback => proc{|value|
298
+ db.escape(value)
299
+ },
300
+ :sep => ",",
301
+ :surr => "'"
302
+ )
303
+ sql_where << " AND #{table}`#{db.esc_col(match[1])}` NOT IN (#{escape_sql})"
304
+ end
305
+ else
306
+ sql_where << " AND #{table}`#{db.esc_col(match[1])}` != '#{db.esc(val)}'"
307
+ end
308
+ elsif match[2] == "lower"
309
+ sql_where << " AND LOWER(#{table}`#{db.esc_col(match[1])}`) = LOWER('#{db.esc(val)}')"
310
+ else
311
+ raise "Unknown mode: '#{match[2]}'."
312
+ end
313
+
314
+ found = true
315
+ elsif args.key?(:cols_date) and match = key.match(/^(.+)_(day|week|month|year|from|to|below|above)(|_(not))$/) and args[:cols_date].index(match[1]) != nil
316
+ not_v = match[4]
317
+ val = Datet.in(val) if val.is_a?(Time)
318
+
319
+ if match[2] == "day"
320
+ if val.is_a?(Array)
321
+ sql_where << " AND ("
322
+ first = true
323
+
324
+ val.each do |realval|
325
+ if first
326
+ first = false
327
+ else
328
+ sql_where << " OR "
329
+ end
330
+
331
+ sql_where << "#{db.sqlspecs.strftime("%d %m %Y", "#{table}`#{db.esc_col(match[1])}`")} #{self.not(not_v, "!")}= #{db.sqlspecs.strftime("%d %m %Y", "'#{db.esc(realval.dbstr)}'")}"
332
+ end
333
+
334
+ sql_where << ")"
335
+ else
336
+ sql_where << " AND #{db.sqlspecs.strftime("%d %m %Y", "#{table}`#{db.esc_col(match[1])}`")} #{self.not(not_v, "!")}= #{db.sqlspecs.strftime("%d %m %Y", "'#{db.esc(val.dbstr)}'")}"
337
+ end
338
+ elsif match[2] == "week"
339
+ sql_where << " AND #{db.sqlspecs.strftime("%W %Y", "#{table}`#{db.esc_col(match[1])}`")} #{self.not(not_v, "!")}= #{db.sqlspecs.strftime("%W %Y", "'#{db.esc(val.dbstr)}'")}"
340
+ elsif match[2] == "month"
341
+ sql_where << " AND #{db.sqlspecs.strftime("%m %Y", "#{table}`#{db.esc_col(match[1])}`")} #{self.not(not_v, "!")}= #{db.sqlspecs.strftime("%m %Y", "'#{db.esc(val.dbstr)}'")}"
342
+ elsif match[2] == "year"
343
+ sql_where << " AND #{db.sqlspecs.strftime("%Y", "#{table}`#{db.esc_col(match[1])}`")} #{self.not(not_v, "!")}= #{db.sqlspecs.strftime("%Y", "'#{db.esc(val.dbstr)}'")}"
344
+ elsif match[2] == "from" or match[2] == "above"
345
+ sql_where << " AND #{table}`#{db.esc_col(match[1])}` >= '#{db.esc(val.dbstr)}'"
346
+ elsif match[2] == "to" or match[2] == "below"
347
+ sql_where << " AND #{table}`#{db.esc_col(match[1])}` <= '#{db.esc(val.dbstr)}'"
348
+ else
349
+ raise "Unknown date-key: #{match[2]}."
350
+ end
351
+
352
+ found = true
353
+ elsif args.key?(:cols_num) and match = key.match(/^(.+)_(from|to|above|below|numeric)$/) and args[:cols_num].index(match[1]) != nil
354
+ if match[2] == "from"
355
+ sql_where << " AND #{table}`#{db.esc_col(match[1])}` >= '#{db.esc(val)}'"
356
+ elsif match[2] == "to"
357
+ sql_where << " AND #{table}`#{db.esc_col(match[1])}` <= '#{db.esc(val)}'"
358
+ elsif match[2] == "above"
359
+ sql_where << " AND #{table}`#{db.esc_col(match[1])}` > '#{db.esc(val)}'"
360
+ elsif match[2] == "below"
361
+ sql_where << " AND #{table}`#{db.esc_col(match[1])}` < '#{db.esc(val)}'"
362
+ else
363
+ raise "Unknown method of treating cols-num-argument: #{match[2]}."
364
+ end
365
+
366
+ found = true
367
+ elsif match = key.match(/^(.+)_lookup$/) and args[:cols].key?("#{match[1]}_id") and args[:cols].key?("#{match[1]}_class")
368
+ sql_where << " AND #{table}`#{db.esc_col("#{match[1]}_class")}` = '#{db.esc(val.table)}'"
369
+ sql_where << " AND #{table}`#{db.esc_col("#{match[1]}_id")}` = '#{db.esc(val.id)}'"
370
+ found = true
371
+ elsif realkey == "groupby"
372
+ found = true
373
+
374
+ if val.is_a?(Array)
375
+ val.each do |col_name|
376
+ raise "Column '#{val}' not found on table '#{table}'." if !args[:cols].key?(col_name)
377
+ sql_groupby << ", " if sql_groupby.length > 0
378
+ sql_groupby << "#{table}`#{db.esc_col(col_name)}`"
379
+ end
380
+ elsif val.is_a?(String)
381
+ sql_groupby << ", " if sql_groupby.length > 0
382
+ sql_groupby << "#{table}`#{db.esc_col(val)}`"
383
+ else
384
+ raise "Unknown class given for 'groupby': '#{val.class.name}'."
385
+ end
386
+ end
387
+
388
+ list_args.delete(realkey) if found
389
+ end
390
+
391
+ args = args_def
392
+
393
+ if !args[:joins_skip]
394
+ raise "No joins defined on '#{args[:table]}' for: '#{args[:table]}'." if !do_joins.empty? and !args[:joined_tables]
395
+
396
+ do_joins.each do |table_name, temp_val|
397
+ raise "No join defined on table '#{args[:table]}' for table '#{table_name}'." if !args[:joined_tables].key?(table_name)
398
+ table_data = args[:joined_tables][table_name]
399
+
400
+ if table_data.key?(:parent_table)
401
+ join_table_name_real = table_name
402
+ sql_joins << " LEFT JOIN `#{table_data[:parent_table]}` AS `#{table_name}` ON 1=1"
403
+ else
404
+ const = @args[:module].const_get(table_name)
405
+ join_table_name_real = const.classname
406
+ sql_joins << " LEFT JOIN `#{const.table}` AS `#{const.classname}` ON 1=1"
407
+ end
408
+
409
+ if table_data[:ob]
410
+ ob = table_data[:ob]
411
+ else
412
+ ob = self
413
+ end
414
+
415
+ class_name = args[:table].to_sym
416
+
417
+ if table_data[:datarow]
418
+ datarow = self.datarow_from_datarow_argument(table_data[:datarow])
419
+ else
420
+ self.requireclass(class_name) if @objects.key?(class_name)
421
+ datarow = @args[:module].const_get(class_name)
422
+ end
423
+
424
+ if !datarow.columns_sqlhelper_args
425
+ ob.requireclass(datarow.table.to_sym)
426
+ raise "No SQL-helper-args on class '#{datarow.table}' ???" if !datarow.columns_sqlhelper_args
427
+ end
428
+
429
+ newargs = datarow.columns_sqlhelper_args.clone
430
+ newargs[:table] = join_table_name_real
431
+ newargs[:joins_skip] = true
432
+
433
+ #Clone the where-arguments and run them against another sqlhelper to sub-join.
434
+ join_args = table_data[:where].clone
435
+ ret = self.sqlhelper(join_args, newargs)
436
+ sql_joins << ret[:sql_where]
437
+
438
+ #If any of the join-arguments are left, then we should throw an error.
439
+ join_args.each do |key, val|
440
+ raise "Invalid key '#{key}' when trying to join table '#{table_name}' on table '#{args_def[:table]}'."
441
+ end
442
+ end
443
+ end
444
+
445
+ #If limit arguments has been given then add them.
446
+ if limit_from and limit_to
447
+ sql_limit = " LIMIT #{limit_from}, #{limit_to}"
448
+ end
449
+
450
+ sql_groupby = nil if sql_groupby.length <= 0
451
+
452
+ return {
453
+ :sql_joins => sql_joins,
454
+ :sql_where => sql_where,
455
+ :sql_limit => sql_limit,
456
+ :sql_order => sql_order,
457
+ :sql_groupby => sql_groupby
458
+ }
459
+ end
460
+
461
+ #Used by sqlhelper-method to look up datarow-classes and automatically load them if they arent loaded already.
462
+ def datarow_obj_from_args(args, list_args, class_name)
463
+ class_name = class_name.to_sym
464
+
465
+ if !args.key?(:joined_tables)
466
+ raise "No joined tables on '#{args[:table]}' to find datarow for: '#{class_name}'."
467
+ end
468
+
469
+ args[:joined_tables].each do |table_name, table_data|
470
+ next if table_name.to_sym != class_name
471
+ return self.datarow_from_datarow_argument(table_data[:datarow]) if table_data[:datarow]
472
+
473
+ self.requireclass(class_name) if @objects.key?(class_name)
474
+ return @args[:module].const_get(class_name)
475
+ end
476
+
477
+ raise "Could not figure out datarow for: '#{class_name}'."
478
+ end
479
+
480
+ def datarow_from_datarow_argument(datarow_argument)
481
+ if datarow_argument.is_a?(String)
482
+ const = Knj::Strings.const_get_full(datarow_argument)
483
+ else
484
+ const = datarow_argument
485
+ end
486
+
487
+ self.load_class(datarow_argument.to_s.split("::").last) if !const.initialized? #Make sure the class is initialized.
488
+
489
+ return const
490
+ end
491
+
492
+ def not(not_v, val)
493
+ if not_v == "not" or not_v == "not_"
494
+ return val
495
+ end
496
+
497
+ return ""
498
+ end
499
+ end