imparcial 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/lib/imparcial/driver/base/expression/base.rb +104 -0
  2. data/lib/imparcial/driver/base/expression/delete.rb +72 -0
  3. data/lib/imparcial/driver/base/expression/index.rb +199 -0
  4. data/lib/imparcial/driver/base/expression/insert.rb +33 -0
  5. data/lib/imparcial/driver/base/expression/lock.rb +11 -0
  6. data/lib/imparcial/driver/base/expression/select.rb +36 -0
  7. data/lib/imparcial/driver/base/expression/sequence.rb +189 -0
  8. data/lib/imparcial/driver/base/expression/statement.rb +128 -0
  9. data/lib/imparcial/driver/base/expression/table_diff.rb +154 -0
  10. data/lib/imparcial/driver/base/expression/table_evolution.rb +94 -0
  11. data/lib/imparcial/driver/base/expression/table_metadata.rb +122 -0
  12. data/lib/imparcial/driver/base/expression/table_operation.rb +137 -0
  13. data/lib/imparcial/driver/base/expression/transaction.rb +59 -0
  14. data/lib/imparcial/driver/base/expression/update.rb +33 -0
  15. data/lib/imparcial/driver/base/expression/util.rb +45 -0
  16. data/lib/imparcial/driver/base/expression.rb +37 -0
  17. data/lib/imparcial/driver/base/result.rb +94 -0
  18. data/lib/imparcial/driver/base/sql/delete.rb +22 -0
  19. data/lib/imparcial/driver/base/sql/index.rb +53 -0
  20. data/lib/imparcial/driver/base/sql/insert.rb +63 -0
  21. data/lib/imparcial/driver/base/sql/select.rb +101 -0
  22. data/lib/imparcial/driver/base/sql/sequence.rb +55 -0
  23. data/lib/imparcial/driver/base/sql/table_metadata.rb +29 -0
  24. data/lib/imparcial/driver/base/sql/table_operation.rb +49 -0
  25. data/lib/imparcial/driver/base/sql/transaction.rb +43 -0
  26. data/lib/imparcial/driver/base/sql/update.rb +29 -0
  27. data/lib/imparcial/driver/base/sql.rb +25 -0
  28. data/lib/imparcial/driver/base/typemap.rb +214 -0
  29. data/lib/imparcial/driver/base/util.rb +41 -0
  30. data/lib/imparcial/driver/base.rb +156 -0
  31. data/lib/imparcial/driver/mysql/expression/index.rb +44 -0
  32. data/lib/imparcial/driver/mysql/expression/table.rb +26 -0
  33. data/lib/imparcial/driver/mysql/expression.rb +11 -0
  34. data/lib/imparcial/driver/mysql/result.rb +33 -0
  35. data/lib/imparcial/driver/mysql/sql/index.rb +51 -0
  36. data/lib/imparcial/driver/mysql/sql/sequence.rb +39 -0
  37. data/lib/imparcial/driver/mysql/sql/table_metadata.rb +43 -0
  38. data/lib/imparcial/driver/mysql/sql/table_operation.rb +20 -0
  39. data/lib/imparcial/driver/mysql/sql.rb +15 -0
  40. data/lib/imparcial/driver/mysql/typemap.rb +13 -0
  41. data/lib/imparcial/driver/mysql/util.rb +13 -0
  42. data/lib/imparcial/driver/mysql.rb +48 -0
  43. data/lib/imparcial/driver/postgre/expression/index.rb +10 -0
  44. data/lib/imparcial/driver/postgre/expression/sequence.rb +9 -0
  45. data/lib/imparcial/driver/postgre/expression/table.rb +20 -0
  46. data/lib/imparcial/driver/postgre/expression.rb +13 -0
  47. data/lib/imparcial/driver/postgre/result.rb +35 -0
  48. data/lib/imparcial/driver/postgre/sql/index.rb +53 -0
  49. data/lib/imparcial/driver/postgre/sql/sequence.rb +28 -0
  50. data/lib/imparcial/driver/postgre/sql/table_metadata.rb +46 -0
  51. data/lib/imparcial/driver/postgre/sql/table_operation.rb +9 -0
  52. data/lib/imparcial/driver/postgre/sql.rb +15 -0
  53. data/lib/imparcial/driver/postgre/typemap.rb +29 -0
  54. data/lib/imparcial/driver/postgre/util.rb +19 -0
  55. data/lib/imparcial/driver/postgre.rb +43 -0
  56. data/lib/imparcial/driver.rb +1 -0
  57. data/lib/imparcial/exception.rb +61 -0
  58. data/lib/imparcial.rb +82 -0
  59. metadata +114 -0
@@ -0,0 +1,214 @@
1
+ module Imparcial
2
+ module Driver
3
+ module TypemapBase
4
+
5
+ public
6
+
7
+ # Those are types shared among all databases.
8
+ # Of course they are not enough. Feel free to override them
9
+ # if necessary.
10
+
11
+ def regular_types
12
+
13
+ {
14
+ :integer => 'INTEGER',
15
+ :string => 'VARCHAR',
16
+ :float => 'FLOAT',
17
+ :datetime => 'DATETIME',
18
+ :time => 'TIME',
19
+ :date => 'DATE',
20
+ :text => 'TEXT',
21
+ :boolean => 'TINYINT',
22
+ :serial => 'SERIAL'
23
+ }
24
+
25
+ end
26
+
27
+ # The opposite of above.
28
+
29
+ def sql_types
30
+
31
+ regular_types.invert
32
+
33
+ end
34
+
35
+ # Default size can be very handy when users are lazy to supply them
36
+ # or they expect a given type to be in a default size.
37
+
38
+ def default_size_for_types
39
+
40
+ {
41
+ :string => 255,
42
+ :boolean => 1
43
+ }
44
+
45
+ end
46
+
47
+ public
48
+
49
+ # Transform regular user defined hash fields into a proper one.
50
+
51
+ def parse_field ( field )
52
+
53
+ # No name? error!
54
+
55
+ if not field[:name]
56
+
57
+ raise OptionError.new('A name must be supplied')
58
+
59
+ end
60
+
61
+ # No type? error!
62
+
63
+ if not field[:type]
64
+
65
+ raise OptionError.new('A type must be supplied')
66
+
67
+ end
68
+
69
+ # Simple conversions.
70
+
71
+ field[:name] = field[:name].to_s
72
+ field[:type] = field[:type].to_sym
73
+
74
+ # Set default size if nothing has been supplied.
75
+
76
+ field[:size] = default_size_for_types[field[:type]] unless field[:size]
77
+
78
+ # Primary key is set as false.
79
+
80
+ field[:pk] ||= false
81
+
82
+ # Allow Null is set as true.
83
+
84
+ field[:allow_null] = true if field[:allow_null] == nil
85
+
86
+ # let's verify if everything is okay.
87
+
88
+ if field[:auto_increment] && !field[:pk]
89
+
90
+ raise OptionError.new("To auto increment field must be primary key")
91
+
92
+ end
93
+
94
+ field[:indexed] ||= false
95
+
96
+ end
97
+
98
+ # In order to work with fields in RDBAL, you have to provide
99
+ # field datas in hashes.
100
+ # This function is basically evaluate them and pass into a block.
101
+
102
+ def parse_fields ( fields = [], &block )
103
+
104
+ # At least a field must be supplied.
105
+
106
+ raise OptionError.new unless fields
107
+ raise OptionError.new if fields.length < 1
108
+
109
+ for field in fields
110
+
111
+ parse_field field
112
+ yield field
113
+
114
+ end
115
+
116
+ end
117
+
118
+ # Transform a regular SQL column into RDBAL's hash.
119
+
120
+ def parse_column ( column )
121
+
122
+ # Apply the appropriate type.
123
+
124
+ type = sql_types[column[:type].upcase]
125
+
126
+ if not type
127
+
128
+ raise OptionError.new('Cannot map column type: ' + column[:type].to_s)
129
+
130
+ end
131
+
132
+ column[:type] = type
133
+
134
+ if column[:size]
135
+
136
+ column[:size] = unquote_value(column[:size])
137
+
138
+ end
139
+
140
+ # Let's work on default value.
141
+
142
+ if column[:default_value]
143
+
144
+ column[:default_value].gsub!("'",'')
145
+
146
+ end
147
+
148
+ # Allow null?
149
+
150
+ if column[:allow_null] == 't' || column[:allow_null] == 1
151
+
152
+ column[:allow_null] = true
153
+
154
+ else
155
+
156
+ column[:allow_null] = false
157
+
158
+ end
159
+
160
+ # Is primary key?
161
+
162
+ if column[:pk] == 't' || column[:pk] == 1
163
+
164
+ column[:pk] = true
165
+
166
+ else
167
+
168
+ column[:pk] = false
169
+
170
+ end
171
+
172
+ # Is automagically incremented?
173
+
174
+ if column[:auto_increment] == 1
175
+
176
+ column[:auto_increment] = true
177
+
178
+ else
179
+
180
+ column[:auto_increment] = false
181
+
182
+ end
183
+
184
+ if column[:indexed] == 't' || column[:indexed] == 1
185
+
186
+ column[:indexed] = true
187
+
188
+ else
189
+
190
+ column[:indexed] = false
191
+
192
+ end
193
+
194
+ end
195
+
196
+ # Iterate columns by parsing them.
197
+
198
+ def parse_columns ( columns, &block )
199
+
200
+ raise OptionError.new unless columns
201
+ raise OptionError.new if columns.length < 1
202
+
203
+ for column in columns
204
+
205
+ parse_column column
206
+ yield column
207
+
208
+ end
209
+
210
+ end
211
+
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,41 @@
1
+ module Imparcial
2
+ module Driver
3
+ module UtilBase
4
+
5
+ # This method possibly will work for many databases.
6
+
7
+ def quote_value ( val )
8
+
9
+ if val.class == String || val.class == Symbol
10
+
11
+ "\"#{val}\""
12
+
13
+ else
14
+
15
+ val.to_s
16
+
17
+ end
18
+
19
+ end
20
+
21
+ # This method possibly will work for many databases.
22
+
23
+ def unquote_value ( val )
24
+
25
+ return 0 if val == '0'
26
+
27
+ val.to_i == 0 ? val : val.to_i
28
+
29
+ end
30
+
31
+ # Subclasses need to override it.
32
+
33
+ def quote ( val )
34
+
35
+ raise FeatureNotFound
36
+
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,156 @@
1
+ require 'imparcial/driver/base/util'
2
+ require 'imparcial/driver/base/typemap'
3
+ require 'imparcial/driver/base/result'
4
+ require 'imparcial/driver/base/sql'
5
+ require 'imparcial/driver/base/expression'
6
+
7
+ module Imparcial
8
+ module Driver
9
+
10
+ # You have seriously to pay attention on this class.
11
+ # Basically speaking, it servers as base for all new drivers.
12
+ # The idea behind Imparcial is to keep an abstract interface without
13
+ # exposing any database-specific-features.
14
+ # Of course, it's an impossible mission anyhow. There are so many
15
+ # databases out there and there are so many cool features that
16
+ # make impossible not to expose them.
17
+ # We recommend you to obey the interface as much as possible.
18
+ # However you may develop some specific features.
19
+
20
+ class AdapterBase
21
+
22
+ # Include SQL syntax generation.
23
+ # Here where all SQL syntax lies on.
24
+
25
+ include SQLBase
26
+
27
+ # Include the expression mechanism.
28
+ # Things like create table, drop table, select and so forth.
29
+ # They all come from this module.
30
+
31
+ include ExpressionBase
32
+
33
+ # Include some util methods.
34
+ # Things like quoting columns, tables and values.
35
+
36
+ include UtilBase
37
+
38
+ # Include Typemap mechanism.
39
+
40
+ include TypemapBase
41
+
42
+ # Let's keep a track of the initializer. We'll need some info eventually.
43
+
44
+ attr_reader :initializer
45
+
46
+ # Let's also keep a track of the connection.
47
+
48
+ attr_accessor :conn
49
+ alias_method :connection, :conn
50
+
51
+ attr_reader :host, :user, :pass, :database, :socket, :port
52
+
53
+ def initialize ( initializer )
54
+
55
+ # Cannot accept classes different from Initializer.
56
+
57
+ if initializer.class != Imparcial::Initializer
58
+
59
+ msg = 'An initializer is needed in order to start a driver'
60
+ raise AdapterConfigError.new(msg)
61
+
62
+ end
63
+
64
+ @initializer = initializer
65
+
66
+ @host = initializer.host
67
+
68
+ if not initializer.user
69
+
70
+ raise AdapterConfigError.new('Adapter requires an username')
71
+
72
+ end
73
+
74
+ @user = initializer.user
75
+
76
+ if not initializer.pass
77
+
78
+ raise AdapterConfigError.new('Adapter requires a password')
79
+
80
+ end
81
+
82
+ @pass = initializer.pass
83
+
84
+ if not initializer.database
85
+
86
+ raise AdapterConfigErrornew('Adapter requires a database')
87
+
88
+ end
89
+
90
+ @database = initializer.database
91
+ @socket = initializer.socket
92
+ @port = initializer.port
93
+
94
+ end
95
+
96
+ #####################################################
97
+ # #
98
+ # Those are methods shared among all databases. #
99
+ # Hardly, one will need to override them. #
100
+ # #
101
+ #####################################################
102
+
103
+ def conn
104
+
105
+ raise AdapterConnectionError.new('Have you connected?') unless @conn
106
+ @conn
107
+
108
+ end
109
+
110
+ def close
111
+
112
+ conn.close
113
+
114
+ end
115
+
116
+ def result
117
+
118
+ raise ResultError.new('Have you made a query?') unless @result
119
+ @result
120
+
121
+ end
122
+
123
+ #####################################################
124
+ # #
125
+ # Subclasses must override those following methods. #
126
+ # They are driver-specific. #
127
+ # #
128
+ #####################################################
129
+
130
+ def connect
131
+
132
+ raise FeatureNotFound
133
+
134
+ end
135
+
136
+ def version
137
+
138
+ raise FeatureNotFound
139
+
140
+ end
141
+
142
+ def query ( sql )
143
+
144
+ raise FeatureNotFound
145
+
146
+ end
147
+
148
+ def adapter_specific_exception
149
+
150
+ raise FeatureNotFound
151
+
152
+ end
153
+
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,44 @@
1
+ module Imparcial
2
+ module Driver
3
+ module ExpressionMysql
4
+ module Index
5
+
6
+ public
7
+
8
+ def drop_all_indexes
9
+
10
+ for index in retrieve_indexes
11
+
12
+ meta = {:index_name => index[:name], :table_name => index[:table]}
13
+ query sql_for_dropping_index(meta)
14
+
15
+ end
16
+
17
+ rescue adapter_specific_exception => ex
18
+
19
+ raise IndexDropError.new(ex.message)
20
+
21
+ end
22
+
23
+ def drop_all_indexes_for_table ( options = {} )
24
+
25
+ check_options expected_options_for_index_table, options
26
+
27
+ for index in retrieve_indexes_for_table options
28
+
29
+ meta = {:index_name => index[:name], :table_name => options[:table_name]}
30
+ query sql_for_dropping_index(meta)
31
+
32
+ end
33
+
34
+ rescue adapter_specific_exception => ex
35
+
36
+ raise IndexDropError.new(ex.message)
37
+
38
+ end
39
+
40
+
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,26 @@
1
+ module Imparcial
2
+ module Driver
3
+ module ExpressionMysql
4
+ module Table
5
+
6
+ private
7
+
8
+ def expected_options_for_creating_table
9
+
10
+ super.merge :engine => :optional
11
+
12
+ end
13
+
14
+ def field_to_column ( field )
15
+
16
+ column = super
17
+ column[:auto_increment] = field[:auto_increment] ? ' AUTO_INCREMENT' : ''
18
+
19
+ column
20
+
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ require 'imparcial/driver/mysql/expression/table'
2
+ require 'imparcial/driver/mysql/expression/index'
3
+
4
+ module Imparcial
5
+ module Driver
6
+ module ExpressionMysql
7
+ include Table
8
+ include Index
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ module Imparcial
2
+ module Driver
3
+ class ResultMysql < ResultBase
4
+
5
+ def rows
6
+
7
+ @specific.num_rows
8
+
9
+ end
10
+
11
+ def fetch
12
+
13
+ fields = @specific.fetch_fields
14
+
15
+ @specific.each do |row|
16
+
17
+ v = []
18
+
19
+ row.each_with_index do |r, index|
20
+
21
+ v << Row.new(fields[index].name, r)
22
+
23
+ end
24
+
25
+ yield(*v)
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,51 @@
1
+ module Imparcial
2
+ module Driver
3
+ module SQLMysql
4
+ module Index
5
+
6
+ private
7
+
8
+ def sql_for_dropping_index ( options )
9
+
10
+ %{
11
+ DROP INDEX #{options[:index_name]} ON
12
+ #{quote(options[:table_name])}
13
+ }
14
+
15
+ end
16
+
17
+ # Generate SQL statement for verifying if a given index exists.
18
+
19
+ def sql_for_index_exists? ( options )
20
+
21
+ %{
22
+ SELECT 1 FROM INFORMATION_SCHEMA.statistics
23
+ WHERE INDEX_NAME = #{quote_value(options[:index_name])}
24
+ }
25
+
26
+ end
27
+
28
+ def sql_for_retrieving_indexes
29
+
30
+ %{
31
+ SELECT
32
+ TABLE_NAME AS 'table',
33
+ COLUMN_NAME AS 'column',
34
+ INDEX_NAME AS 'index'
35
+ FROM INFORMATION_SCHEMA.statistics
36
+ }
37
+
38
+ end
39
+
40
+ def sql_for_retrieving_indexes_for_table ( options )
41
+
42
+ sql_for_retrieving_indexes + %{
43
+ WHERE TABLE_NAME = #{quote_value(options[:table_name])}
44
+ }
45
+
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,39 @@
1
+ module Imparcial
2
+ module Driver
3
+ module SQLMysql
4
+
5
+ # Mysql doesn't support sequences.
6
+ # We have to disable all them.
7
+
8
+ module Sequence
9
+
10
+ private
11
+
12
+ def sql_for_creating_sequence ( options )
13
+
14
+ raise FeatureNotFound
15
+
16
+ end
17
+
18
+ def sql_for_dropping_sequence ( options )
19
+
20
+ raise FeatureNotFound
21
+
22
+ end
23
+
24
+ def sql_for_sequence_exists? ( options )
25
+
26
+ raise FeatureNotFound
27
+
28
+ end
29
+
30
+ def sql_for_retrieving_sequences
31
+
32
+ raise FeatureNotFound
33
+
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,43 @@
1
+ module Imparcial
2
+ module Driver
3
+ module SQLMysql
4
+ module TableMetadata
5
+
6
+ private
7
+
8
+ # Generate SQL statement for retrieving tables.
9
+
10
+ def sql_for_retrieving_tables
11
+
12
+ 'SHOW TABLES'
13
+
14
+ end
15
+
16
+ # Generate SQL statement for retrieving columns.
17
+
18
+ def sql_for_retrieving_columns ( options = {} )
19
+
20
+ %{
21
+ SELECT
22
+ A.column_name, A.data_type, A.character_maximum_length as size,
23
+ (A.is_nullable = 'YES') as allow_null,
24
+ (A.column_name = B.column_name) as pk,
25
+ A.column_default,
26
+ A.extra = 'auto_increment' as auto_inc,
27
+ A.column_name in (
28
+ SELECT s.COLUMN_NAME FROM INFORMATION_SCHEMA.statistics s
29
+ WHERE s.COLUMN_NAME = A.column_name
30
+ ) as indexed
31
+
32
+ FROM information_schema.columns A
33
+ LEFT JOIN information_schema.key_column_usage B ON A.table_name = B.table_name
34
+ WHERE A.table_name = #{quote_value(options[:table_name])}
35
+
36
+ }
37
+
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,20 @@
1
+ module Imparcial
2
+ module Driver
3
+ module SQLMysql
4
+ module TableOperation
5
+
6
+ private
7
+
8
+ def sql_for_creating_table ( options = {} )
9
+
10
+ syntax = super
11
+ syntax += 'ENGINE = ' + options[:engine].to_s if options[:engine]
12
+
13
+ syntax
14
+
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ require 'imparcial/driver/mysql/sql/table_operation'
2
+ require 'imparcial/driver/mysql/sql/table_metadata'
3
+ require 'imparcial/driver/mysql/sql/sequence'
4
+ require 'imparcial/driver/mysql/sql/index'
5
+
6
+ module Imparcial
7
+ module Driver
8
+ module SQLMysql
9
+ include TableOperation
10
+ include TableMetadata
11
+ include Sequence
12
+ include Index
13
+ end
14
+ end
15
+ end