baza 0.0.0

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.
@@ -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