momomoto 0.1.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.
- 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
|
+
|