momomoto 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/LICENSE +340 -0
  2. data/Rakefile +38 -0
  3. data/lib/momomoto.rb +5 -0
  4. data/lib/momomoto/base.rb +162 -0
  5. data/lib/momomoto/database.rb +179 -0
  6. data/lib/momomoto/datatype.rb +20 -0
  7. data/lib/momomoto/datatype/base.rb +78 -0
  8. data/lib/momomoto/datatype/bigint.rb +9 -0
  9. data/lib/momomoto/datatype/boolean.rb +23 -0
  10. data/lib/momomoto/datatype/bytea.rb +13 -0
  11. data/lib/momomoto/datatype/character.rb +7 -0
  12. data/lib/momomoto/datatype/character_varying.rb +7 -0
  13. data/lib/momomoto/datatype/date.rb +22 -0
  14. data/lib/momomoto/datatype/inet.rb +14 -0
  15. data/lib/momomoto/datatype/integer.rb +16 -0
  16. data/lib/momomoto/datatype/interval.rb +30 -0
  17. data/lib/momomoto/datatype/numeric.rb +17 -0
  18. data/lib/momomoto/datatype/real.rb +7 -0
  19. data/lib/momomoto/datatype/smallint.rb +7 -0
  20. data/lib/momomoto/datatype/text.rb +24 -0
  21. data/lib/momomoto/datatype/time_with_time_zone.rb +10 -0
  22. data/lib/momomoto/datatype/time_without_time_zone.rb +30 -0
  23. data/lib/momomoto/datatype/timestamp_with_time_zone.rb +7 -0
  24. data/lib/momomoto/datatype/timestamp_without_time_zone.rb +29 -0
  25. data/lib/momomoto/information_schema/columns.rb +45 -0
  26. data/lib/momomoto/information_schema/fetch_procedure_columns.rb +14 -0
  27. data/lib/momomoto/information_schema/fetch_procedure_parameters.rb +14 -0
  28. data/lib/momomoto/information_schema/key_column_usage.rb +19 -0
  29. data/lib/momomoto/information_schema/routines.rb +12 -0
  30. data/lib/momomoto/information_schema/table_constraints.rb +19 -0
  31. data/lib/momomoto/join.rb +66 -0
  32. data/lib/momomoto/momomoto.rb +10 -0
  33. data/lib/momomoto/order.rb +56 -0
  34. data/lib/momomoto/procedure.rb +129 -0
  35. data/lib/momomoto/row.rb +63 -0
  36. data/lib/momomoto/table.rb +251 -0
  37. data/sql/install.sql +10 -0
  38. data/sql/procedures.sql +54 -0
  39. data/sql/types.sql +11 -0
  40. data/test/test_base.rb +17 -0
  41. data/test/test_bigint.rb +26 -0
  42. data/test/test_boolean.rb +30 -0
  43. data/test/test_bytea.rb +35 -0
  44. data/test/test_character.rb +27 -0
  45. data/test/test_character_varying.rb +17 -0
  46. data/test/test_database.rb +63 -0
  47. data/test/test_datatype.rb +62 -0
  48. data/test/test_date.rb +50 -0
  49. data/test/test_inet.rb +27 -0
  50. data/test/test_information_schema.rb +27 -0
  51. data/test/test_integer.rb +37 -0
  52. data/test/test_interval.rb +38 -0
  53. data/test/test_join.rb +19 -0
  54. data/test/test_numeric.rb +30 -0
  55. data/test/test_procedure.rb +75 -0
  56. data/test/test_real.rb +17 -0
  57. data/test/test_row.rb +47 -0
  58. data/test/test_smallint.rb +26 -0
  59. data/test/test_table.rb +233 -0
  60. data/test/test_text.rb +25 -0
  61. data/test/test_time_with_time_zone.rb +17 -0
  62. data/test/test_time_without_time_zone.rb +40 -0
  63. data/test/test_timestamp_with_time_zone.rb +17 -0
  64. data/test/test_timestamp_without_time_zone.rb +28 -0
  65. metadata +116 -0
@@ -0,0 +1,179 @@
1
+
2
+ begin
3
+ require 'rubygems'
4
+ require_gem 'ruby-postgres', '>= 0.7.1.2006.04.06'
5
+ rescue LoadError
6
+ require 'postgres'
7
+ end
8
+
9
+ require 'singleton'
10
+
11
+ require 'momomoto/information_schema/columns'
12
+ require 'momomoto/information_schema/table_constraints'
13
+ require 'momomoto/information_schema/key_column_usage'
14
+ require 'momomoto/information_schema/routines'
15
+ require 'momomoto/information_schema/fetch_procedure_columns'
16
+ require 'momomoto/information_schema/fetch_procedure_parameters'
17
+
18
+ ## Momomoto is a database abstraction layer
19
+ module Momomoto
20
+
21
+ ## Momomoto Connection class
22
+ class Database
23
+ include Singleton
24
+
25
+ # establish connection to the database
26
+ # expects a hash with the following keys: host, port, database,
27
+ # username, password, pgoptions and pgtty
28
+ def config( config )
29
+ config ||= {}
30
+ # we also accept String keys in the config hash
31
+ config.each do | key, value |
32
+ config[key.to_sym] = value unless key.kind_of?( Symbol )
33
+ config[key.to_sym] = value.to_s if value.kind_of?(Symbol)
34
+ end
35
+ @config = config
36
+ end
37
+
38
+ def self.config( conf )
39
+ instance.config( conf )
40
+ end
41
+
42
+ def initialize # :nodoc:
43
+ @config = {}
44
+ @connection = nil
45
+ end
46
+
47
+ def connect
48
+ @connection.close if @connection
49
+ @transaction_active = false
50
+ PGconn.translate_results = true
51
+ @connection = PGconn.connect( @config[:host], @config[:port], @config[:pgoptions],
52
+ @config[:pgtty], @config[:database], @config[:username],
53
+ @config[:password])
54
+ rescue => e
55
+ raise CriticalError, "Connection to database failed: #{e}"
56
+ end
57
+
58
+ def self.connect
59
+ instance.connect
60
+ end
61
+
62
+ # terminate this connection
63
+ def disconnect
64
+ @connection.close
65
+ @connection = nil
66
+ @transaction_active = true
67
+ end
68
+
69
+ # execute a query
70
+ def execute( sql ) # :nodoc:
71
+ puts sql if Momomoto.debug
72
+ @connection.query( sql )
73
+ rescue => e
74
+ raise CriticalError, "#{e}: #{sql}"
75
+ end
76
+
77
+ # fetch columns which are primary key columns
78
+ # should work with any SQL2003 compliant DBMS
79
+ def fetch_primary_keys( table_name, schema_name = nil ) # :nodoc:
80
+ pkeys = []
81
+ conditions = {:table_name=>table_name, :constraint_type => 'PRIMARY KEY'}
82
+ conditions[:table_schema] = schema_name if schema_name
83
+ keys = Momomoto::Information_schema::Table_constraints.select( conditions )
84
+ if keys.length != 0
85
+ cols = Momomoto::Information_schema::Key_column_usage.select(
86
+ { :table_name => keys[0].table_name,
87
+ :table_schema => keys[0].table_schema,
88
+ :constraint_name => keys[0].constraint_name,
89
+ :constraint_schema => keys[0].constraint_schema } )
90
+ cols.each do | key |
91
+ pkeys << key.column_name.to_sym
92
+ end
93
+ end
94
+ pkeys
95
+ end
96
+
97
+ # fetch column definitions from database
98
+ # should work with any SQL2003 compliant DBMS
99
+ def fetch_table_columns( table_name, schema_name = nil ) # :nodoc:
100
+ columns = {}
101
+ conditions = { :table_name => table_name }
102
+ conditions[:table_schema] = schema_name if schema_name
103
+ cols = Momomoto::Information_schema::Columns.select( conditions )
104
+ raise CriticalError, "Table without columns" if cols.length < 1
105
+ cols.each do | col |
106
+ columns[col.column_name.to_sym] = Momomoto::Datatype.const_get(col.data_type.gsub(' ','_').capitalize).new( col )
107
+ end
108
+ columns
109
+ end
110
+
111
+ # fetches the parameter of a stored procedure
112
+ def fetch_procedure_parameters( procedure_name, schema_name = nil ) # :nodoc:
113
+ p = []
114
+ conditions = { :procedure_name => procedure_name }
115
+ params = Momomoto::Information_schema::Fetch_procedure_parameters.call( conditions )
116
+ params.each do | param |
117
+ p << { param.parameter_name.to_sym => Momomoto::Datatype.const_get(param.data_type.gsub(' ','_').capitalize).new }
118
+ end
119
+ p
120
+ end
121
+
122
+ # fetches the resultset columns of a stored procedure
123
+ def fetch_procedure_columns( procedure_name, schema_name = nil ) # :nodoc:
124
+ c = {}
125
+ conditions = { :procedure_name => procedure_name }
126
+ cols = Momomoto::Information_schema::Fetch_procedure_columns.call( conditions )
127
+ cols.each do | col |
128
+ c[col.column_name.to_sym] = Momomoto::Datatype.const_get(col.data_type.gsub(' ','_').capitalize).new
129
+ end
130
+ c
131
+ end
132
+
133
+
134
+ # begin a transaction
135
+ def begin
136
+ execute( "BEGIN;" )
137
+ @transaction_active = true
138
+ end
139
+
140
+ # executes the block and commits the transaction if a block is given
141
+ # otherwise simply starts a new transaction
142
+ def transaction
143
+ raise Error, "Transaction active" if @transaction_active
144
+ self.begin
145
+ begin
146
+ yield
147
+ rescue => e
148
+ rollback
149
+ raise e
150
+ end
151
+ commit
152
+ end
153
+
154
+ # commit the current transaction
155
+ def commit
156
+ raise Error if not @transaction_active
157
+ execute( "COMMIT;" )
158
+ @transaction_active = false
159
+ end
160
+
161
+ # roll the transaction back
162
+ def rollback
163
+ raise Error if not @transaction_active
164
+ execute( "ROLLBACK;" )
165
+ @transaction_active = false
166
+ end
167
+
168
+ def self.escape_string( input )
169
+ PGconn.escape( input )
170
+ end
171
+
172
+ def self.escape_bytea( input )
173
+ PGconn.escape_bytea( input )
174
+ end
175
+
176
+ end
177
+
178
+ end
179
+
@@ -0,0 +1,20 @@
1
+
2
+ require 'momomoto/datatype/base'
3
+ require 'momomoto/datatype/boolean'
4
+ require 'momomoto/datatype/integer'
5
+ require 'momomoto/datatype/smallint'
6
+ require 'momomoto/datatype/bigint'
7
+ require 'momomoto/datatype/text'
8
+ require 'momomoto/datatype/character'
9
+ require 'momomoto/datatype/character_varying'
10
+ require 'momomoto/datatype/numeric'
11
+ require 'momomoto/datatype/real'
12
+ require 'momomoto/datatype/bytea'
13
+ require 'momomoto/datatype/date'
14
+ require 'momomoto/datatype/time_without_time_zone'
15
+ require 'momomoto/datatype/interval'
16
+ require 'momomoto/datatype/time_with_time_zone'
17
+ require 'momomoto/datatype/timestamp_without_time_zone'
18
+ require 'momomoto/datatype/timestamp_with_time_zone'
19
+ require 'momomoto/datatype/inet'
20
+
@@ -0,0 +1,78 @@
1
+
2
+ module Momomoto
3
+
4
+ module Datatype
5
+ # base class for all datatypes
6
+ class Base
7
+ # get the default value for this column
8
+ # returns false if none exists
9
+ def default
10
+ @default
11
+ end
12
+
13
+ # is this column a not null column
14
+ def not_null?
15
+ @not_null
16
+ end
17
+
18
+ def initialize( row = nil )
19
+ @not_null = row.respond_to?(:is_nullable) && row.is_nullable == "NO"
20
+ @default = row.respond_to?( :column_default) && row.column_default
21
+ end
22
+
23
+ # values are filtered by this function when being set
24
+ def filter_set( value ) # :nodoc:
25
+ value
26
+ end
27
+
28
+ def escape( input )
29
+ input.nil? ? "NULL" : "'" + Database.escape_string( input.to_s ) + "'"
30
+ end
31
+
32
+ # this functions is used for compiling the where clause
33
+ def compile_rule( field_name, value ) # :nodoc:
34
+ case value
35
+ when nil then
36
+ raise Error, "nil values not allowed for #{field_name}"
37
+ when Array then
38
+ raise Error, "empty array conditions are not allowed for #{field_name}" if value.empty?
39
+ raise Error, "nil values not allowed in compile_rule for #{field_name}" if value.member?( nil )
40
+ field_name.to_s + ' IN (' + value.map{ | v | escape(filter_set(v)) }.join(',') + ')'
41
+ when Hash then
42
+ raise Error, "empty hash conditions are not allowed for #{field_name}" if value.empty?
43
+ rules = []
44
+ value.each do | op, v |
45
+ raise Error, "nil values not allowed in compile_rule for #{field_name}" if v == nil
46
+ v = [v] if not v.kind_of?( Array )
47
+ if op == :eq # use IN if comparing for equality
48
+ rules << compile_rule( field_name, v )
49
+ else
50
+ v.each do | v2 |
51
+ rules << field_name.to_s + ' ' + self.class.operator_sign(op) + ' ' + escape(filter_set(v2))
52
+ end
53
+ end
54
+ end
55
+ rules.join( " AND " )
56
+ else
57
+ field_name.to_s + ' = ' + escape(filter_set(value))
58
+ end
59
+ end
60
+
61
+ def self.operator_sign( op )
62
+ case op
63
+ when :le then '<='
64
+ when :lt then '<'
65
+ when :ge then '>='
66
+ when :gt then '>'
67
+ when :eq then '='
68
+ when :ne then '<>'
69
+ else
70
+ raise CriticalError, "unsupported operator"
71
+ end
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+ end
78
+
@@ -0,0 +1,9 @@
1
+ module Momomoto
2
+ module Datatype
3
+
4
+ class Bigint < Integer
5
+
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ module Momomoto
2
+ module Datatype
3
+ class Boolean < Base
4
+
5
+ def filter_set( value )
6
+ case value
7
+ when true, 1, 't', 'true', 'on' then true
8
+ when false, 0, 'f', 'false', 'off' then false
9
+ else not_null? ? false : nil
10
+ end
11
+ end
12
+
13
+ def escape( input )
14
+ case input
15
+ when true, 1, 't', 'true', 'on' then "'t'"
16
+ when false, 0, 'f', 'false', 'off' then "'f'"
17
+ else "NULL"
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+
2
+ module Momomoto
3
+ module Datatype
4
+ class Bytea < Base
5
+
6
+ def escape( input )
7
+ input.nil? ? "NULL" : "'" + Database.escape_bytea( input ) + "'"
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,7 @@
1
+ module Momomoto
2
+ module Datatype
3
+ class Character < Text
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Momomoto
2
+ module Datatype
3
+ class Character_varying < Text
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+
2
+ require 'date'
3
+
4
+ module Momomoto
5
+ module Datatype
6
+ class Date < Base
7
+
8
+ def filter_set( value )
9
+ case value
10
+ when nil,'' then nil
11
+ when ::Date then value
12
+ when String then ::Date.parse( value, '%Y-%m-%d' )
13
+ else raise Error
14
+ end
15
+ rescue => e
16
+ raise ConversionError, "parse Error in Date #{e}"
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,14 @@
1
+ module Momomoto
2
+ module Datatype
3
+ class Inet < Text
4
+
5
+ def filter_set( value )
6
+ case value
7
+ when nil, '' then nil
8
+ else value
9
+ end
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ module Momomoto
2
+ module Datatype
3
+ class Integer < Base
4
+
5
+ def filter_set( value )
6
+ case value
7
+ when nil, '' then nil
8
+ else Integer( value )
9
+ end
10
+ rescue => e
11
+ raise ConversionError, e.to_s
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ module Momomoto
2
+ module Datatype
3
+ class Interval < Time_without_time_zone
4
+
5
+ def escape( value )
6
+ case value
7
+ when nil then 'NULL'
8
+ when String then "'#{Database.escape_string(value)}'"
9
+ else "'#{Database.escape_string(value.strftime('%H:%M:%S'))}'"
10
+ end
11
+ end
12
+
13
+ def filter_get( value )
14
+ case value
15
+ when nil, '' then nil
16
+ when ::Time then value
17
+ when String then ::Time.parse( value )
18
+ else raise Error
19
+ end
20
+ rescue => e
21
+ raise ConversionError, 'Error while parsing time'
22
+ end
23
+
24
+ def filter_set( value )
25
+ filter_get( value )
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+
2
+ module Momomoto
3
+ module Datatype
4
+ class Numeric < Base
5
+
6
+ def filter_set( value )
7
+ case value
8
+ when nil, '' then nil
9
+ else Float( value )
10
+ end
11
+ rescue => e
12
+ raise ConversionError, e.to_s
13
+ end
14
+
15
+ end
16
+ end
17
+ end