SqlStatement 1.0.0 → 1.0.1
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/doc/ChangeLog +14 -0
- data/doc/EXAMPLE +124 -0
- data/lib/sql/dbi-support.rb +25 -0
- data/lib/sql/expression.rb +4 -0
- data/lib/sql/statement.rb +36 -82
- data/lib/sqlstatement.rb +79 -0
- metadata +11 -7
- data/bin/example.rb +0 -129
data/doc/ChangeLog
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
=Change Log
|
2
|
+
==1.0.1
|
3
|
+
* +add_fields+ and +add_tables+ now support hashes too, so no more of
|
4
|
+
this silly <tt>stmt.fields.merge!</tt> stuff is requred anymore
|
5
|
+
* now there's a DBI adaptor that lets DBI accept +SQLStatements+ and
|
6
|
+
process them automatically, so no more of this silly
|
7
|
+
<tt>dbh.do(stmt.to_s,*stmt.placeheld)</tt> you can just say
|
8
|
+
<tt>dbh.do(stmt)</tt> (likewise for +execute+)
|
9
|
+
* fix a bug in string_func which broke it completely.
|
10
|
+
* Documentation fixes
|
11
|
+
* Make <tt>== nil</tt> comparisons turn into <tt>is null</tt> in SQL.
|
12
|
+
|
13
|
+
==1.0.0
|
14
|
+
Initial Release
|
data/doc/EXAMPLE
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
#=Example
|
2
|
+
# # We subclass the standard classes so that we can construct
|
3
|
+
# # a query with a complex computed field. The newlists method
|
4
|
+
# # makes this easy, giving us a constructor and combination
|
5
|
+
# # operators for free.
|
6
|
+
#
|
7
|
+
# class ProbabilityParts < SQLStatement::SelectParts
|
8
|
+
# newlists :multiply
|
9
|
+
# end
|
10
|
+
# class ProbabilityStatement < SQLStatement::SelectCreate
|
11
|
+
# newlists :multiply
|
12
|
+
#
|
13
|
+
# # Add our own special fields here, computed
|
14
|
+
# # from the list we added to this class.
|
15
|
+
# def allfields
|
16
|
+
# retval=super
|
17
|
+
# retval.merge! :probability =>
|
18
|
+
# SQLFunc(@multiply.collect{|x| x.to_sqlpart}.join('*'))
|
19
|
+
# retval
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# #In real life, I use a special library for detecting what attributes
|
25
|
+
# #are available based on the columns in my tables.
|
26
|
+
# #We'll mimic that functionality here.
|
27
|
+
# #
|
28
|
+
# #The first definition of this module mocks the stuff that's in my more
|
29
|
+
# #complicated library
|
30
|
+
# module AttributeDetection
|
31
|
+
# class Attribute
|
32
|
+
# def initialize(domain,name)
|
33
|
+
# @domain=domain
|
34
|
+
# @name=name
|
35
|
+
# end
|
36
|
+
# attr_reader :name, :domain
|
37
|
+
# end
|
38
|
+
# class Hierarchy < Attribute
|
39
|
+
# def initialize(leftcolumn)
|
40
|
+
# ignored1,m_domain,m_name,ignored2=leftcolumn.split(/_/)
|
41
|
+
# super(m_domain,m_name)
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
# class DomainlessPerGroup < Attribute
|
45
|
+
# def initialize (colname)
|
46
|
+
# name=@colname=colname
|
47
|
+
# domain="!"
|
48
|
+
# super(domain,name)
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# #I have a few other attribute types, none of which
|
53
|
+
# #are used in this code yet.
|
54
|
+
#
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# include SQLHelpers
|
58
|
+
#
|
59
|
+
# #The second definition of this module is the code that I actually define
|
60
|
+
# #in this particular program.
|
61
|
+
# module AttributeDetection
|
62
|
+
# class Attribute
|
63
|
+
# def condname
|
64
|
+
# :"cond_#{name}"
|
65
|
+
# end
|
66
|
+
# def priorname
|
67
|
+
# :"prior_#{name}"
|
68
|
+
# end
|
69
|
+
# #parts that go in to the CREATE TABLE `probabilities` statement
|
70
|
+
# #which relate to the attribute we are disambiguating
|
71
|
+
# def probability_disambig
|
72
|
+
# parts=ProbabilityParts.new
|
73
|
+
# parts.add_fields [priorname[probkey]]
|
74
|
+
# parts.add_tables [priorname]
|
75
|
+
# parts.multiply << priorname[:pt]
|
76
|
+
# parts
|
77
|
+
# end
|
78
|
+
# #parts that go in to the CREATE TABLE `probabilities` statement
|
79
|
+
# #which relate to the attributes we are using as features
|
80
|
+
# def probability_features(conditionalon)
|
81
|
+
# parts=ProbabilityParts.new
|
82
|
+
# parts.tables={priorname=>priorname,condname=>condname}
|
83
|
+
# parts.add_fields [priorname[probkey]]
|
84
|
+
# parts.conditions << sql_func{ condname[probkey]==priorname[probkey]}
|
85
|
+
# parts.conditions << sql_func{
|
86
|
+
# condname[conditionalon.probkey]==
|
87
|
+
# conditionalon.priorname[conditionalon.probkey]
|
88
|
+
# }
|
89
|
+
# parts.multiply << sql_func{condname[:pt]/priorname[:pt]}
|
90
|
+
# parts
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
# class Hierarchy
|
94
|
+
# def probkey
|
95
|
+
# "att_#{domain}_#{name}_left".to_sym
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
# class DomainlessPerGroup
|
99
|
+
# def probkey
|
100
|
+
# @colname.to_sym
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# include AttributeDetection
|
106
|
+
# include SQLHelpers
|
107
|
+
#
|
108
|
+
# #And this code calls methods to actually build the query.
|
109
|
+
#
|
110
|
+
# #First we define the names of the attributes
|
111
|
+
# att_disambig=Hierarchy.new("att_appraisal_attitude_left")
|
112
|
+
# att_features=[Hierarchy.new("att_products_appraisedtype_left"),
|
113
|
+
# DomainlessPerGroup.new("priority")]
|
114
|
+
#
|
115
|
+
#
|
116
|
+
# #Then we construct the statement
|
117
|
+
# stmt=ProbabilityStatement.new :probabilities,true
|
118
|
+
#
|
119
|
+
# stmt << att_disambig.probability_disambig
|
120
|
+
# att_features.each { |x| stmt<< x.probability_features(att_disambig) }
|
121
|
+
#
|
122
|
+
# #Finally, we execute it against the database. Assume dbh is a
|
123
|
+
# #dbi connection handle.
|
124
|
+
# dbh.execute(stmt.to_s,*stmt.placeheld)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#Redefine DBI's query methods to accept SQL statement objects from
|
2
|
+
#this library. This is automatically loaded if DBI was loaded before
|
3
|
+
#+sqlstatement+. If not, <tt>require 'sql/dbi-support'</tt> after DBI
|
4
|
+
#is included and you'll be on your way.
|
5
|
+
class DBI::DatabaseHandle
|
6
|
+
alias_method :kenbloom_sqlstatement_old_do, :"do"
|
7
|
+
alias_method :kenbloom_sqlstatement_old_execute, :execute
|
8
|
+
|
9
|
+
def do(stmt,*args)
|
10
|
+
if stmt.respond_to?(:placeheld)
|
11
|
+
kenbloom_sqlstatement_old_do(stmt.to_s,*stmt.placeheld)
|
12
|
+
else
|
13
|
+
kenbloom_sqlstatement_old_do(stmt,*args)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
def execute(stmt,*args)
|
17
|
+
if stmt.respond_to?(:placeheld)
|
18
|
+
kenbloom_sqlstatement_old_execute(stmt.to_s,*stmt.placeheld)
|
19
|
+
else
|
20
|
+
kenbloom_sqlstatement_old_execute(stmt,*args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
data/lib/sql/expression.rb
CHANGED
@@ -12,6 +12,10 @@ class Array
|
|
12
12
|
"count(distinct "+rest_sqlpart+")"
|
13
13
|
elsif first.to_s =~ /[a-zA-Z]/
|
14
14
|
first.to_s+"("+rest_sqlpart+")"
|
15
|
+
elsif first == :== and self[2]==nil
|
16
|
+
self[1].to_sqlpart+" is null"
|
17
|
+
elsif first == :== and self[1]==nil
|
18
|
+
self[2].to_sqlpart+" is null"
|
15
19
|
elsif x={:& => " and ", :| => " or ", :== => "="}[first]
|
16
20
|
"("+self[1].to_sqlpart+x+self[2].to_sqlpart+")"
|
17
21
|
else
|
data/lib/sql/statement.rb
CHANGED
@@ -1,70 +1,3 @@
|
|
1
|
-
=begin rdoc
|
2
|
-
= sqlstatement - Generate complex SQL statements programmatically
|
3
|
-
|
4
|
-
The main goal of this library is to be able to construct an SQL statement
|
5
|
-
from "slices" that concern different aspects of the final query (perhaps
|
6
|
-
in different places in your code) and then combine them all together into
|
7
|
-
one statement easily.
|
8
|
-
|
9
|
-
Another important goal of this library is to give some consistent Ruby
|
10
|
-
syntax to three statements (INSERT, SELECT, and UPDATE) that seem to have
|
11
|
-
different enough syntax that one has two write different code to generate
|
12
|
-
each kind of statement.
|
13
|
-
|
14
|
-
I use my SQL database (specifically MySQL) largely as a bulk data
|
15
|
-
processing engine, by doing INSERT...SELECT or CREATE TABLE...SELECT
|
16
|
-
statements. This library is intended to make that kind of coding easier. I
|
17
|
-
expect that Object Relational mappers (such as ActiveRecord) are more
|
18
|
-
useful for most people, who are performing queries and
|
19
|
-
inserting/updating/querying for individual records. I have nevertheless
|
20
|
-
added INSERT...VALUES statements, and will add other statements soon, for
|
21
|
-
consistency.
|
22
|
-
|
23
|
-
This library is inspired by CLSQL[http://clsql.b9.com/] for Common LISP,
|
24
|
-
or SchemeQL[http://schematics.sourceforge.net/schemeql.html] for Scheme,
|
25
|
-
although it is very different from these two libraries. Scheme and
|
26
|
-
LISP's use of s-expressions make it very easy to construct an entire
|
27
|
-
sublanguage for the WHERE clause, simply by list parsing. The
|
28
|
-
Criteria[http://mephle.org/Criteria/] library for Ruby has attempted
|
29
|
-
this, but in a more limited manner than SchemeQL or CLSQL. My library
|
30
|
-
aims to cover much of the functionality in these libraries.
|
31
|
-
|
32
|
-
This library doesn't try to abstract out the limitations of your DBMS, and
|
33
|
-
I think that the SQL it uses should be fairly portable, in large measure
|
34
|
-
because it hasn't attempted to deal with serious CREATE TABLE statements,
|
35
|
-
where a lot of syntax concerning types, keys and sequences is much more
|
36
|
-
variable.
|
37
|
-
|
38
|
-
==License
|
39
|
-
|
40
|
-
Copyright (c) 2006 Ken Bloom
|
41
|
-
All rights reserved.
|
42
|
-
|
43
|
-
Redistribution and use in source and binary forms, with or without
|
44
|
-
modification, are permitted provided that the following conditions
|
45
|
-
are met:
|
46
|
-
1. Redistributions of source code must retain the above copyright
|
47
|
-
notice, this list of conditions and the following disclaimer.
|
48
|
-
2. Redistributions in binary form must reproduce the above copyright
|
49
|
-
notice, this list of conditions and the following disclaimer in the
|
50
|
-
documentation and/or other materials provided with the distribution.
|
51
|
-
3. Neither Ken Bloom's name, nor the name of any of his contributors may
|
52
|
-
may be used to endorse or promote products derived from this software
|
53
|
-
without specific prior written permission.
|
54
|
-
|
55
|
-
THIS SOFTWARE IS PROVIDED BY THE KEN BLOOM AND CONTRIBUTORS ``AS IS'' AND
|
56
|
-
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
57
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
58
|
-
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
59
|
-
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
60
|
-
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
61
|
-
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
62
|
-
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
63
|
-
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
64
|
-
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
65
|
-
SUCH DAMAGE.
|
66
|
-
=end
|
67
|
-
|
68
1
|
# Everything in Ruby is a descendant of the Object class. Practically
|
69
2
|
# speaking, this means that any object that hasn't otherwise defined
|
70
3
|
# the +placeheld+ and <tt>to_sqlpart</tt> methods will be treated as a piece
|
@@ -107,6 +40,15 @@ class Symbol
|
|
107
40
|
end
|
108
41
|
end
|
109
42
|
|
43
|
+
class NilClass
|
44
|
+
def to_sqlpart
|
45
|
+
"null"
|
46
|
+
end
|
47
|
+
def placeheld
|
48
|
+
[]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
110
52
|
module SQLStatement
|
111
53
|
|
112
54
|
# This class is used to represent complex SQL expressions, so that
|
@@ -118,7 +60,7 @@ module SQLStatement
|
|
118
60
|
class Function < String
|
119
61
|
|
120
62
|
def initialize(val="",placeheld=[])
|
121
|
-
super
|
63
|
+
super val
|
122
64
|
@placeheld=placeheld
|
123
65
|
end
|
124
66
|
|
@@ -220,19 +162,24 @@ class SelectParts
|
|
220
162
|
#See the documentation for SelectStatement which describes these.
|
221
163
|
attr_accessor :conditions, :groupby, :orderby, :fields, :tables
|
222
164
|
|
223
|
-
#Adds an array of unaliased fields
|
224
|
-
#documentation at SelectStatement
|
165
|
+
#Adds an array of unaliased fields, or a hash of aliased fields to
|
166
|
+
#this SQL statement. See documentation at SelectStatement
|
225
167
|
def add_fields (newfields)
|
226
|
-
newfields.
|
227
|
-
@fields
|
168
|
+
if newfields.is_a?(Hash)
|
169
|
+
@fields.merge! newfields
|
170
|
+
else
|
171
|
+
newfields.each { |x| @fields[x]=x }
|
228
172
|
end
|
173
|
+
nil
|
229
174
|
end
|
230
175
|
|
231
|
-
#Adds an array of unaliased tables
|
232
|
-
#documentation for SelectStatement
|
176
|
+
#Adds an array of unaliased tables or a hash of aliased tables to
|
177
|
+
#this SQL statement. See documentation for SelectStatement
|
233
178
|
def add_tables (newtables)
|
234
|
-
newtables.
|
235
|
-
@tables
|
179
|
+
if newtables.is_a?(Hash)
|
180
|
+
@tables.merge! newtables
|
181
|
+
else
|
182
|
+
newtables.each { |x| @tables[x]=x }
|
236
183
|
end
|
237
184
|
end
|
238
185
|
|
@@ -297,11 +244,15 @@ class Select
|
|
297
244
|
#aliasing a field name, use the form <tt>fieldname => fieldname</tt>.
|
298
245
|
attr_accessor :fields
|
299
246
|
|
300
|
-
#Adds an array of unaliased fields
|
247
|
+
#Adds an array of unaliased fields, or a hash of aliased fields to
|
248
|
+
#this SQL statement.
|
301
249
|
def add_fields (newfields)
|
302
|
-
newfields.
|
303
|
-
@fields
|
250
|
+
if newfields.is_a?(Hash)
|
251
|
+
@fields.merge! newfields
|
252
|
+
else
|
253
|
+
newfields.each { |x| @fields[x]=x }
|
304
254
|
end
|
255
|
+
nil
|
305
256
|
end
|
306
257
|
|
307
258
|
#This is the tables to include in the query (i.e. the +FROM+ clause).
|
@@ -311,10 +262,13 @@ class Select
|
|
311
262
|
#aliasing a table name, use the form <tt>tablename => tablename</tt>.
|
312
263
|
attr_accessor :tables
|
313
264
|
|
314
|
-
#Adds an array of unaliased tables
|
265
|
+
#Adds an array of unaliased tables or a hash of aliased tables to
|
266
|
+
#this SQL statement.
|
315
267
|
def add_tables (newtables)
|
316
|
-
newtables.
|
317
|
-
@tables
|
268
|
+
if newtables.is_a?(Hash)
|
269
|
+
@tables.merge! newtables
|
270
|
+
else
|
271
|
+
newtables.each { |x| @tables[x]=x }
|
318
272
|
end
|
319
273
|
end
|
320
274
|
|
data/lib/sqlstatement.rb
CHANGED
@@ -1,2 +1,81 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
= sqlstatement - Generate complex SQL statements programmatically
|
3
|
+
|
4
|
+
The main goal of this library is to be able to construct an SQL statement
|
5
|
+
from "slices" that concern different aspects of the final query (perhaps
|
6
|
+
in different places in your code) and then combine them all together into
|
7
|
+
one statement easily.
|
8
|
+
|
9
|
+
Another important goal of this library is to give some consistent Ruby
|
10
|
+
syntax to three statements (INSERT, SELECT, and UPDATE) that seem to have
|
11
|
+
different enough syntax that one has two write different code to generate
|
12
|
+
each kind of statement.
|
13
|
+
|
14
|
+
I use my SQL database (specifically MySQL) largely as a bulk data
|
15
|
+
processing engine, by doing INSERT...SELECT or CREATE TABLE...SELECT
|
16
|
+
statements. This library is intended to make that kind of coding easier. I
|
17
|
+
expect that Object Relational mappers (such as ActiveRecord) are more
|
18
|
+
useful for most people, who are performing queries and
|
19
|
+
inserting/updating/querying for individual records. I have nevertheless
|
20
|
+
added INSERT...VALUES statements, and will add other statements soon, for
|
21
|
+
consistency.
|
22
|
+
|
23
|
+
This library is inspired by CLSQL[http://clsql.b9.com/] for Common LISP,
|
24
|
+
or SchemeQL[http://schematics.sourceforge.net/schemeql.html] for Scheme,
|
25
|
+
although it is very different from these two libraries. Scheme and
|
26
|
+
LISP's use of s-expressions make it very easy to construct an entire
|
27
|
+
sublanguage for the WHERE clause, simply by list parsing. The
|
28
|
+
Criteria[http://mephle.org/Criteria/] library for Ruby has attempted
|
29
|
+
this, but in a more limited manner than SchemeQL or CLSQL. My library
|
30
|
+
aims to cover much of the functionality in these libraries.
|
31
|
+
|
32
|
+
This library doesn't try to abstract out the limitations of your DBMS, and
|
33
|
+
I think that the SQL it uses should be fairly portable, in large measure
|
34
|
+
because it hasn't attempted to deal with serious CREATE TABLE statements,
|
35
|
+
where a lot of syntax concerning types, keys and sequences is much more
|
36
|
+
variable.
|
37
|
+
|
38
|
+
This library can be downloaded from
|
39
|
+
http://rubyforge.org/projects/sqlstatement/
|
40
|
+
|
41
|
+
==Author
|
42
|
+
Ken Bloom <mailto:kbloom@gmail.com>
|
43
|
+
|
44
|
+
http://www.iit.edu/~kbloom1/
|
45
|
+
|
46
|
+
==License
|
47
|
+
|
48
|
+
Copyright (c) 2006 Ken Bloom
|
49
|
+
All rights reserved.
|
50
|
+
|
51
|
+
Redistribution and use in source and binary forms, with or without
|
52
|
+
modification, are permitted provided that the following conditions
|
53
|
+
are met:
|
54
|
+
1. Redistributions of source code must retain the above copyright
|
55
|
+
notice, this list of conditions and the following disclaimer.
|
56
|
+
2. Redistributions in binary form must reproduce the above copyright
|
57
|
+
notice, this list of conditions and the following disclaimer in the
|
58
|
+
documentation and/or other materials provided with the distribution.
|
59
|
+
3. Neither Ken Bloom's name, nor the name of any of his contributors may
|
60
|
+
may be used to endorse or promote products derived from this software
|
61
|
+
without specific prior written permission.
|
62
|
+
|
63
|
+
THIS SOFTWARE IS PROVIDED BY THE KEN BLOOM AND CONTRIBUTORS ``AS IS'' AND
|
64
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
65
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
66
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
67
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
68
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
69
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
70
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
71
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
72
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
73
|
+
SUCH DAMAGE.
|
74
|
+
=end
|
75
|
+
|
76
|
+
|
1
77
|
require 'sql/statement'
|
2
78
|
require 'sql/expression'
|
79
|
+
if defined?(DBI)
|
80
|
+
require 'sql/dbi-support'
|
81
|
+
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: SqlStatement
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.0.
|
7
|
-
date: 2006-
|
6
|
+
version: 1.0.1
|
7
|
+
date: 2006-11-20 00:00:00 -06:00
|
8
8
|
summary: A library for generating arbitrary SQL statements using convenient Ruby objects.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -29,18 +29,22 @@ post_install_message:
|
|
29
29
|
authors:
|
30
30
|
- Ken Bloom
|
31
31
|
files:
|
32
|
-
- bin/example.rb
|
33
32
|
- lib/sql
|
34
33
|
- lib/sqlstatement.rb
|
35
34
|
- lib/sql/expression.rb
|
36
35
|
- lib/sql/bathon-sxp.rb
|
37
36
|
- lib/sql/statement.rb
|
37
|
+
- lib/sql/dbi-support.rb
|
38
|
+
- doc/EXAMPLE
|
39
|
+
- doc/ChangeLog
|
38
40
|
test_files: []
|
39
41
|
|
40
|
-
rdoc_options:
|
41
|
-
|
42
|
-
|
43
|
-
|
42
|
+
rdoc_options:
|
43
|
+
- --main
|
44
|
+
- lib/sqlstatement.rb
|
45
|
+
extra_rdoc_files:
|
46
|
+
- doc/EXAMPLE
|
47
|
+
- doc/ChangeLog
|
44
48
|
executables: []
|
45
49
|
|
46
50
|
extensions: []
|
data/bin/example.rb
DELETED
@@ -1,129 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require_gem 'SqlStatement'
|
3
|
-
require 'sql/statement'
|
4
|
-
require 'sql/expression'
|
5
|
-
|
6
|
-
# We subclass the standard classes so that we can construct
|
7
|
-
# a query with a complex computed field. The newlists method
|
8
|
-
# makes this easy, giving us a constructor and combination
|
9
|
-
# operators for free.
|
10
|
-
|
11
|
-
class ProbabilityParts < SQLStatement::SelectParts
|
12
|
-
newlists :multiply
|
13
|
-
end
|
14
|
-
class ProbabilityStatement < SQLStatement::SelectCreate
|
15
|
-
newlists :multiply
|
16
|
-
|
17
|
-
# Add our own special fields here, computed
|
18
|
-
# from the list we added to this class.
|
19
|
-
def allfields
|
20
|
-
retval=super
|
21
|
-
retval.merge! :probability =>
|
22
|
-
SQLFunc(@multiply.collect{|x| x.to_sqlpart}.join('*'))
|
23
|
-
retval
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
|
28
|
-
#In real life, I use a special library for detecting what attributes
|
29
|
-
#are available based on the columns in my tables.
|
30
|
-
#We'll mimic that functionality here.
|
31
|
-
#
|
32
|
-
#The first definition of this module mocks the stuff that's in my more
|
33
|
-
#complicated library
|
34
|
-
module AttributeDetection
|
35
|
-
class Attribute
|
36
|
-
def initialize(domain,name)
|
37
|
-
@domain=domain
|
38
|
-
@name=name
|
39
|
-
end
|
40
|
-
attr_reader :name, :domain
|
41
|
-
end
|
42
|
-
class Hierarchy < Attribute
|
43
|
-
def initialize(leftcolumn)
|
44
|
-
ignored1,m_domain,m_name,ignored2=leftcolumn.split(/_/)
|
45
|
-
super(m_domain,m_name)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
class DomainlessPerGroup < Attribute
|
49
|
-
def initialize (colname)
|
50
|
-
name=@colname=colname
|
51
|
-
domain="!"
|
52
|
-
super(domain,name)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
#I have a few other attribute types, none of which
|
57
|
-
#are used in this code yet.
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
include SQLHelpers
|
62
|
-
|
63
|
-
#The second definition of this module is the code that I actually define
|
64
|
-
#in this particular program.
|
65
|
-
module AttributeDetection
|
66
|
-
class Attribute
|
67
|
-
def condname
|
68
|
-
:"cond_#{name}"
|
69
|
-
end
|
70
|
-
def priorname
|
71
|
-
:"prior_#{name}"
|
72
|
-
end
|
73
|
-
#parts that go in to the CREATE TABLE `probabilities` statement
|
74
|
-
#which relate to the attribute we are disambiguating
|
75
|
-
def probability_disambig
|
76
|
-
parts=ProbabilityParts.new
|
77
|
-
parts.add_fields [priorname[probkey]]
|
78
|
-
parts.add_tables [priorname]
|
79
|
-
parts.multiply << priorname[:pt]
|
80
|
-
parts
|
81
|
-
end
|
82
|
-
#parts that go in to the CREATE TABLE `probabilities` statement
|
83
|
-
#which relate to the attributes we are using as features
|
84
|
-
def probability_features(conditionalon)
|
85
|
-
parts=ProbabilityParts.new
|
86
|
-
parts.tables={priorname=>priorname,condname=>condname}
|
87
|
-
parts.add_fields [priorname[probkey]]
|
88
|
-
parts.conditions << sql_func{ condname[probkey]==priorname[probkey]}
|
89
|
-
parts.conditions << sql_func{
|
90
|
-
condname[conditionalon.probkey]==
|
91
|
-
conditionalon.priorname[conditionalon.probkey]
|
92
|
-
}
|
93
|
-
parts.multiply << sql_func{condname[:pt]/priorname[:pt]}
|
94
|
-
parts
|
95
|
-
end
|
96
|
-
end
|
97
|
-
class Hierarchy
|
98
|
-
def probkey
|
99
|
-
"att_#{domain}_#{name}_left".to_sym
|
100
|
-
end
|
101
|
-
end
|
102
|
-
class DomainlessPerGroup
|
103
|
-
def probkey
|
104
|
-
@colname.to_sym
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
include AttributeDetection
|
110
|
-
include SQLHelpers
|
111
|
-
|
112
|
-
#And this code calls methods to actually build the query.
|
113
|
-
|
114
|
-
#First we define the names of the attributes
|
115
|
-
att_disambig=Hierarchy.new("att_appraisal_attitude_left")
|
116
|
-
att_features=[Hierarchy.new("att_products_appraisedtype_left"),
|
117
|
-
DomainlessPerGroup.new("priority")]
|
118
|
-
|
119
|
-
|
120
|
-
#Then we construct the statement
|
121
|
-
stmt=ProbabilityStatement.new :probabilities,true
|
122
|
-
|
123
|
-
stmt << att_disambig.probability_disambig
|
124
|
-
att_features.each { |x| stmt<< x.probability_features(att_disambig) }
|
125
|
-
|
126
|
-
#Finally, we print it (or execute it against the database)
|
127
|
-
puts stmt.to_s
|
128
|
-
|
129
|
-
|