momomoto 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +340 -0
- data/Rakefile +38 -0
- data/lib/momomoto.rb +5 -0
- data/lib/momomoto/base.rb +162 -0
- data/lib/momomoto/database.rb +179 -0
- data/lib/momomoto/datatype.rb +20 -0
- data/lib/momomoto/datatype/base.rb +78 -0
- data/lib/momomoto/datatype/bigint.rb +9 -0
- data/lib/momomoto/datatype/boolean.rb +23 -0
- data/lib/momomoto/datatype/bytea.rb +13 -0
- data/lib/momomoto/datatype/character.rb +7 -0
- data/lib/momomoto/datatype/character_varying.rb +7 -0
- data/lib/momomoto/datatype/date.rb +22 -0
- data/lib/momomoto/datatype/inet.rb +14 -0
- data/lib/momomoto/datatype/integer.rb +16 -0
- data/lib/momomoto/datatype/interval.rb +30 -0
- data/lib/momomoto/datatype/numeric.rb +17 -0
- data/lib/momomoto/datatype/real.rb +7 -0
- data/lib/momomoto/datatype/smallint.rb +7 -0
- data/lib/momomoto/datatype/text.rb +24 -0
- data/lib/momomoto/datatype/time_with_time_zone.rb +10 -0
- data/lib/momomoto/datatype/time_without_time_zone.rb +30 -0
- data/lib/momomoto/datatype/timestamp_with_time_zone.rb +7 -0
- data/lib/momomoto/datatype/timestamp_without_time_zone.rb +29 -0
- data/lib/momomoto/information_schema/columns.rb +45 -0
- data/lib/momomoto/information_schema/fetch_procedure_columns.rb +14 -0
- data/lib/momomoto/information_schema/fetch_procedure_parameters.rb +14 -0
- data/lib/momomoto/information_schema/key_column_usage.rb +19 -0
- data/lib/momomoto/information_schema/routines.rb +12 -0
- data/lib/momomoto/information_schema/table_constraints.rb +19 -0
- data/lib/momomoto/join.rb +66 -0
- data/lib/momomoto/momomoto.rb +10 -0
- data/lib/momomoto/order.rb +56 -0
- data/lib/momomoto/procedure.rb +129 -0
- data/lib/momomoto/row.rb +63 -0
- data/lib/momomoto/table.rb +251 -0
- data/sql/install.sql +10 -0
- data/sql/procedures.sql +54 -0
- data/sql/types.sql +11 -0
- data/test/test_base.rb +17 -0
- data/test/test_bigint.rb +26 -0
- data/test/test_boolean.rb +30 -0
- data/test/test_bytea.rb +35 -0
- data/test/test_character.rb +27 -0
- data/test/test_character_varying.rb +17 -0
- data/test/test_database.rb +63 -0
- data/test/test_datatype.rb +62 -0
- data/test/test_date.rb +50 -0
- data/test/test_inet.rb +27 -0
- data/test/test_information_schema.rb +27 -0
- data/test/test_integer.rb +37 -0
- data/test/test_interval.rb +38 -0
- data/test/test_join.rb +19 -0
- data/test/test_numeric.rb +30 -0
- data/test/test_procedure.rb +75 -0
- data/test/test_real.rb +17 -0
- data/test/test_row.rb +47 -0
- data/test/test_smallint.rb +26 -0
- data/test/test_table.rb +233 -0
- data/test/test_text.rb +25 -0
- data/test/test_time_with_time_zone.rb +17 -0
- data/test/test_time_without_time_zone.rb +40 -0
- data/test/test_timestamp_with_time_zone.rb +17 -0
- data/test/test_timestamp_without_time_zone.rb +28 -0
- metadata +116 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module Momomoto
|
2
|
+
module Datatype
|
3
|
+
class Text < Base
|
4
|
+
|
5
|
+
def escape( input )
|
6
|
+
if input.nil? || input.empty?
|
7
|
+
"NULL"
|
8
|
+
else
|
9
|
+
"'" + Database.escape_string( input.to_s ) + "'"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.operator_sign( op )
|
14
|
+
case op
|
15
|
+
when :like then 'LIKE'
|
16
|
+
when :ilike then 'ILIKE'
|
17
|
+
else
|
18
|
+
super( op )
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Momomoto
|
5
|
+
module Datatype
|
6
|
+
class Time_without_time_zone < Base
|
7
|
+
|
8
|
+
def escape( value )
|
9
|
+
case value
|
10
|
+
when nil then 'NULL'
|
11
|
+
when String then "'#{Database.escape_string(value)}'"
|
12
|
+
else "'#{Database.escape_string(value.strftime('%H:%M:%S'))}'"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def filter_set( value )
|
17
|
+
case value
|
18
|
+
when nil, '' then nil
|
19
|
+
when ::Time then value
|
20
|
+
when String then ::Time.parse( value )
|
21
|
+
else raise Error
|
22
|
+
end
|
23
|
+
rescue => e
|
24
|
+
raise ConversionError, 'Error while parsing time'
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module Momomoto
|
5
|
+
module Datatype
|
6
|
+
class Timestamp_without_time_zone < Base
|
7
|
+
|
8
|
+
def escape( value )
|
9
|
+
case value
|
10
|
+
when nil then 'NULL'
|
11
|
+
when String then "'#{Database.escape_string(value)}'"
|
12
|
+
else "'#{Database.escape_string(value.strftime('%Y-%m-%d %H:%M:%S'))}'"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def filter_set( value )
|
17
|
+
case value
|
18
|
+
when nil, '' then nil
|
19
|
+
when ::Time then value
|
20
|
+
when String then ::Time.parse( value )
|
21
|
+
else raise Error
|
22
|
+
end
|
23
|
+
rescue => e
|
24
|
+
raise ConversionError, "Error while parsing Timestamp"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
module Momomoto
|
3
|
+
module Information_schema
|
4
|
+
class Columns < Momomoto::Table
|
5
|
+
primary_keys( [] )
|
6
|
+
schema_name( "information_schema" )
|
7
|
+
columns( { :table_catalog => Momomoto::Datatype::Text.new,
|
8
|
+
:table_schema => Momomoto::Datatype::Text.new,
|
9
|
+
:table_name => Momomoto::Datatype::Text.new,
|
10
|
+
:column_name => Momomoto::Datatype::Text.new,
|
11
|
+
:ordinal_position => Momomoto::Datatype::Integer.new,
|
12
|
+
:column_default => Momomoto::Datatype::Text.new,
|
13
|
+
:is_nullable => Momomoto::Datatype::Text.new,
|
14
|
+
:data_type => Momomoto::Datatype::Text.new,
|
15
|
+
:character_maximum_length => Momomoto::Datatype::Integer.new,
|
16
|
+
:character_octet_length => Momomoto::Datatype::Integer.new,
|
17
|
+
:numeric_precision => Momomoto::Datatype::Integer.new,
|
18
|
+
:numeric_precision_radix => Momomoto::Datatype::Integer.new,
|
19
|
+
:numeric_scale => Momomoto::Datatype::Integer.new,
|
20
|
+
:datetime_precision => Momomoto::Datatype::Integer.new,
|
21
|
+
:interval_type => Momomoto::Datatype::Character_varying.new,
|
22
|
+
:interval_precision => Momomoto::Datatype::Character_varying.new,
|
23
|
+
:character_set_catalog => Momomoto::Datatype::Character_varying.new,
|
24
|
+
:character_set_schema => Momomoto::Datatype::Character_varying.new,
|
25
|
+
:character_set_name => Momomoto::Datatype::Character_varying.new,
|
26
|
+
:collation_catalog => Momomoto::Datatype::Character_varying.new,
|
27
|
+
:collation_schema => Momomoto::Datatype::Character_varying.new,
|
28
|
+
:collation_name => Momomoto::Datatype::Character_varying.new,
|
29
|
+
:domain_catalog => Momomoto::Datatype::Character_varying.new,
|
30
|
+
:domain_schema => Momomoto::Datatype::Character_varying.new,
|
31
|
+
:domain_name => Momomoto::Datatype::Character_varying.new,
|
32
|
+
:udt_catalog => Momomoto::Datatype::Character_varying.new,
|
33
|
+
:udt_schema => Momomoto::Datatype::Character_varying.new,
|
34
|
+
:udt_name => Momomoto::Datatype::Character_varying.new,
|
35
|
+
:scope_catalog => Momomoto::Datatype::Character_varying.new,
|
36
|
+
:scope_schema => Momomoto::Datatype::Character_varying.new,
|
37
|
+
:scope_name => Momomoto::Datatype::Character_varying.new,
|
38
|
+
:maximum_cardinality => Momomoto::Datatype::Integer.new,
|
39
|
+
:dtd_identifier => Momomoto::Datatype::Character_varying.new,
|
40
|
+
:is_self_referencing => Momomoto::Datatype::Character_varying.new
|
41
|
+
} )
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
module Momomoto
|
3
|
+
module Information_schema
|
4
|
+
class Fetch_procedure_columns < Momomoto::Procedure
|
5
|
+
schema_name( "momomoto" )
|
6
|
+
parameters( :procedure_name => Momomoto::Datatype::Text.new )
|
7
|
+
columns( {
|
8
|
+
:column_name => Momomoto::Datatype::Text.new,
|
9
|
+
:data_type => Momomoto::Datatype::Text.new
|
10
|
+
} )
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
module Momomoto
|
3
|
+
module Information_schema
|
4
|
+
class Fetch_procedure_parameters < Momomoto::Procedure
|
5
|
+
schema_name( "momomoto" )
|
6
|
+
parameters( :procedure_name => Momomoto::Datatype::Text.new )
|
7
|
+
columns( {
|
8
|
+
:parameter_name => Momomoto::Datatype::Text.new,
|
9
|
+
:data_type => Momomoto::Datatype::Text.new
|
10
|
+
} )
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
module Momomoto
|
3
|
+
module Information_schema
|
4
|
+
class Key_column_usage < Momomoto::Table
|
5
|
+
primary_keys( [] )
|
6
|
+
schema_name( "information_schema" )
|
7
|
+
columns( { :constraint_catalog => Momomoto::Datatype::Character_varying.new,
|
8
|
+
:constraint_schema => Momomoto::Datatype::Character_varying.new,
|
9
|
+
:constraint_name => Momomoto::Datatype::Character_varying.new,
|
10
|
+
:table_catalog => Momomoto::Datatype::Character_varying.new,
|
11
|
+
:table_schema => Momomoto::Datatype::Character_varying.new,
|
12
|
+
:table_name => Momomoto::Datatype::Character_varying.new,
|
13
|
+
:column_name => Momomoto::Datatype::Character_varying.new,
|
14
|
+
:ordinal_position => Momomoto::Datatype::Integer.new
|
15
|
+
} )
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
module Momomoto
|
3
|
+
module Information_schema
|
4
|
+
class Table_constraints < Momomoto::Table
|
5
|
+
primary_keys( [] )
|
6
|
+
schema_name( "information_schema" )
|
7
|
+
columns( { :constraint_catalog => Momomoto::Datatype::Text.new,
|
8
|
+
:constraint_schema => Momomoto::Datatype::Text.new,
|
9
|
+
:constraint_name => Momomoto::Datatype::Text.new,
|
10
|
+
:table_catalog => Momomoto::Datatype::Text.new,
|
11
|
+
:table_schema => Momomoto::Datatype::Text.new,
|
12
|
+
:table_name => Momomoto::Datatype::Text.new,
|
13
|
+
:constraint_type => Momomoto::Datatype::Text.new,
|
14
|
+
:is_deferrable => Momomoto::Datatype::Text.new,
|
15
|
+
:initially_deferred => Momomoto::Datatype::Text.new
|
16
|
+
} )
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
module Momomoto
|
3
|
+
|
4
|
+
# this class implements the join functionality it is an abstract class
|
5
|
+
# it must not be used directly but you should inherit from this class
|
6
|
+
class Join < Base
|
7
|
+
|
8
|
+
attr_reader :base_table, :join_rules
|
9
|
+
|
10
|
+
# constructor of the join class
|
11
|
+
# base_table is the base table of the join
|
12
|
+
# join_rules is an array of Hashes consisting of the table to join as key
|
13
|
+
# and an symbol or an array of symbols on which fields to join the table
|
14
|
+
# Example: Momomoto::Join.new(Event, {Event_Person=>:event_id},{Person=>:person_id})
|
15
|
+
# results in SELECT * FROM event INNER JOIN event_person USING (event_id) INNER JOIN Person USING(person_id)
|
16
|
+
# if you leave out the eclipit braces to mark the hash you may get
|
17
|
+
# unexpected results because of the undefined order of hashs
|
18
|
+
def initialize( base_table, *join_rules )
|
19
|
+
@base_table = base_table
|
20
|
+
@join_rules = join_rules
|
21
|
+
|
22
|
+
metaclass.instance_eval do
|
23
|
+
define_method( base_table.table_name ) do base_table end
|
24
|
+
end
|
25
|
+
join_rules.each do | rule |
|
26
|
+
rule.keys.each do | table, join_columns |
|
27
|
+
metaclass.instance_eval do
|
28
|
+
define_method( table.table_name ) do table end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def metaclass # :nodoc:
|
35
|
+
class << self; self; end
|
36
|
+
end
|
37
|
+
|
38
|
+
def class_variable_set( variable, value ) # :nodoc:
|
39
|
+
self.class.send( :class_variable_set, variable, value )
|
40
|
+
end
|
41
|
+
|
42
|
+
def select( conditions = {}, options = {} )
|
43
|
+
sql = "SELECT " + base_table.columns.keys.map{ | field | "#{base_table.table_name}.\"#{field}\"" }.join( "," )
|
44
|
+
join_rules.each do | rules |
|
45
|
+
rules.each do | table, fields |
|
46
|
+
sql += ','
|
47
|
+
sql += table.columns.keys.map{ | field | "#{table.table_name}.\"#{field}\"" }.join( ',' )
|
48
|
+
end
|
49
|
+
end
|
50
|
+
sql += " FROM "
|
51
|
+
sql += base_table.full_name
|
52
|
+
join_rules.each do | rules |
|
53
|
+
rules.each do | table_name, fields |
|
54
|
+
fields = fields.class === Array ? fields : [fields]
|
55
|
+
sql += " INNER JOIN #{table_name} USING(#{fields.join(', ')})"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
sql += self.class.compile_where( conditions )
|
59
|
+
@data = []
|
60
|
+
self.class.database.execute( sql )
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
module Momomoto
|
3
|
+
|
4
|
+
class Order
|
5
|
+
|
6
|
+
attr_accessor :fields
|
7
|
+
|
8
|
+
def initialize( *fields )
|
9
|
+
@fields = fields.flatten
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_sql( columns )
|
13
|
+
sql = []
|
14
|
+
fields.each do | field |
|
15
|
+
if field.kind_of?( Momomoto::Order )
|
16
|
+
sql << function( field.to_sql( columns ) )
|
17
|
+
else
|
18
|
+
raise Momomoto::Error, "Unknown field #{field} in order" if not columns.keys.member?( field.to_sym )
|
19
|
+
sql << function( field )
|
20
|
+
end
|
21
|
+
end
|
22
|
+
sql.join(',')
|
23
|
+
end
|
24
|
+
|
25
|
+
class Lower < Order
|
26
|
+
def initialize( *fields )
|
27
|
+
fields = fields.flatten
|
28
|
+
fields.each do | field |
|
29
|
+
raise Error, "Asc and Desc are only allowed as toplevel order elements" if field.kind_of?( Asc ) or field.kind_of?( Desc )
|
30
|
+
end
|
31
|
+
@fields = fields
|
32
|
+
end
|
33
|
+
|
34
|
+
def function( argument )
|
35
|
+
argument = [ argument ] if not argument.kind_of?( Array )
|
36
|
+
argument = argument.map{|a| "lower(#{a})"}
|
37
|
+
argument.join(',')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Asc < Order
|
42
|
+
def function( argument )
|
43
|
+
"#{argument} asc"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Desc < Order
|
48
|
+
def function( argument )
|
49
|
+
"#{argument} desc"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,129 @@
|
|
1
|
+
|
2
|
+
module Momomoto
|
3
|
+
|
4
|
+
# this class implements access to stored procedures
|
5
|
+
# it must not be used directly but you should inherit from this class
|
6
|
+
class Procedure < Base
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def initialize_procedure # :nodoc:
|
11
|
+
|
12
|
+
unless class_variables.member?( '@@procedure_name' )
|
13
|
+
procedure_name( construct_procedure_name( self.name ) )
|
14
|
+
end
|
15
|
+
|
16
|
+
unless class_variables.member?( '@@schema_name' )
|
17
|
+
schema_name( construct_schema_name( self.name ) )
|
18
|
+
end
|
19
|
+
|
20
|
+
unless class_variables.member?( '@@parameters' )
|
21
|
+
parameters( database.fetch_procedure_parameters( procedure_name ) )
|
22
|
+
raise CriticalError if not parameters
|
23
|
+
end
|
24
|
+
|
25
|
+
unless class_variables.member?( '@@columns' )
|
26
|
+
columns( database.fetch_procedure_columns( procedure_name ) )
|
27
|
+
raise CriticalError if not columns
|
28
|
+
end
|
29
|
+
|
30
|
+
const_set( :Row, Class.new( Momomoto::Row ) ) if not const_defined?( :Row )
|
31
|
+
initialize_row( const_get( :Row ), self )
|
32
|
+
|
33
|
+
# mark class as initialized
|
34
|
+
class_variable_set( :@@initialized, true)
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
# guesses the procedure name of the procedure this class works on
|
39
|
+
def construct_procedure_name( classname ) # :nodoc:
|
40
|
+
classname.split('::').last.downcase.gsub(/[^a-z_0-9]/, '')
|
41
|
+
end
|
42
|
+
|
43
|
+
# set the procedure name
|
44
|
+
def procedure_name=( procedure_name )
|
45
|
+
class_variable_set( :@@procedure_name, procedure_name )
|
46
|
+
end
|
47
|
+
|
48
|
+
# get the procedure name
|
49
|
+
def procedure_name( procedure_name = nil )
|
50
|
+
return self.procedure_name=( procedure_name ) if procedure_name
|
51
|
+
begin
|
52
|
+
class_variable_get( :@@procedure_name )
|
53
|
+
rescue NameError
|
54
|
+
construct_procedure_name( self.name )
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# get the full name of the procedure including schema if set
|
59
|
+
def full_name # :nodoc:
|
60
|
+
"#{ schema_name ? schema_name + '.' : ''}#{procedure_name}"
|
61
|
+
end
|
62
|
+
|
63
|
+
# set the parameters this procedures accepts
|
64
|
+
# example: parameters = {:param1=>Momomoto::Datatype::Text.new}
|
65
|
+
# example: parameters = {:param1=>Momomoto::Datatype::Text.new}
|
66
|
+
def parameters=( *p )
|
67
|
+
p = p.flatten
|
68
|
+
class_variable_set( :@@parameters, p )
|
69
|
+
end
|
70
|
+
|
71
|
+
# get the parameters this procedure accepts
|
72
|
+
def parameters( *p )
|
73
|
+
return self.send( :parameters=, *p ) if not p.empty?
|
74
|
+
begin
|
75
|
+
class_variable_get( :@@parameters )
|
76
|
+
rescue NameError
|
77
|
+
initialize_procedure
|
78
|
+
class_variable_get( :@@parameters )
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# get the columns of the resultset this procedure returns
|
83
|
+
def columns=( columns )
|
84
|
+
class_variable_set( :@@columns, columns)
|
85
|
+
end
|
86
|
+
|
87
|
+
# get the columns of the resultset this procedure returns
|
88
|
+
def columns( c = nil )
|
89
|
+
return self.columns=( c ) if c
|
90
|
+
begin
|
91
|
+
class_variable_get( :@@columns )
|
92
|
+
rescue NameError
|
93
|
+
initialize_procedure
|
94
|
+
class_variable_get( :@@columns )
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def compile_parameter( params ) # :nodoc:
|
99
|
+
args = []
|
100
|
+
parameters.each do | parameter |
|
101
|
+
field_name, datatype = parameter.to_a.first
|
102
|
+
raise Error, "parameter #{field_name} not specified" if not params.member?( field_name )
|
103
|
+
args << datatype.escape( params[field_name] )
|
104
|
+
end
|
105
|
+
args.join(',')
|
106
|
+
end
|
107
|
+
|
108
|
+
# execute the stored procedure
|
109
|
+
def call( params = {}, conditions = {}, options = {} )
|
110
|
+
initialize_procedure unless class_variables.member?('@@initialized')
|
111
|
+
sql = "SELECT #{columns.keys.join(',')} FROM "
|
112
|
+
sql += "#{full_name}(#{compile_parameter(params)})"
|
113
|
+
sql += compile_where( conditions )
|
114
|
+
sql += compile_order( options[:order] ) if options[:order]
|
115
|
+
sql += compile_limit( options[:limit] ) if options[:limit]
|
116
|
+
sql += ';'
|
117
|
+
data = []
|
118
|
+
database.execute( sql ).each do | row |
|
119
|
+
data << const_get(:Row).new( row )
|
120
|
+
end
|
121
|
+
data
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|