mystic 0.0.9 → 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.
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pg"
4
+ require "access_stack"
5
+
6
+ module Mystic
7
+ class Postgres
8
+ CONNECT_FIELDS = [
9
+ :host,
10
+ :hostaddr,
11
+ :port,
12
+ :dbname,
13
+ :user,
14
+ :password,
15
+ :connect_timeout,
16
+ :options,
17
+ :tty,
18
+ :sslmode,
19
+ :krbsrvname,
20
+ :gsslib
21
+ ].freeze
22
+
23
+ INDEX_TYPES = [
24
+ :btree,
25
+ :hash,
26
+ :gist,
27
+ :spgist,
28
+ :gin
29
+ ].freeze
30
+
31
+ def initialize opts={}
32
+ return if opts.empty?
33
+ @pool = AccessStack.new(
34
+ :size => opts[:pool] || 5,
35
+ :timeout => opts[:timeout] || 30,
36
+ :expires => opts[:expires],
37
+ :create => lambda { create_pg opts },
38
+ :destroy => lambda { |pg| pg.close },
39
+ :validate => lambda { |pg| pg != nil && pg.status == CONNECTION_OK }
40
+ )
41
+ end
42
+
43
+ alias_method :connect, :initialize
44
+
45
+ def pool_size= v
46
+ @pool.size = v
47
+ end
48
+
49
+ def disconnect
50
+ @pool.empty!
51
+ end
52
+
53
+ def reap!
54
+ @pool.reap!
55
+ end
56
+
57
+ def connected?
58
+ !@pool.empty?
59
+ end
60
+
61
+ def escape str
62
+ @pool.with { |pg| pg.escape_string str }
63
+ end
64
+
65
+ def execute sql
66
+ res = @pool.with { |pg| pg.exec sql }
67
+ v = res[0][Mystic::JSON_COL] if res.ntuples == 1 && res.nfields == 1
68
+ v ||= res.ntuples.times.map { |i| res[i] } unless res.nil?
69
+ v ||= []
70
+ v
71
+ end
72
+
73
+ def create_pg opts
74
+ pg = PG.connect opts.subhash(*CONNECT_FIELDS)
75
+ pg.set_notice_receiver { |r| }
76
+ pg
77
+ end
78
+ end
79
+ end
@@ -2,243 +2,9 @@
2
2
 
3
3
  module Mystic
4
4
  module SQL
5
- Error = Class.new(StandardError)
6
-
7
- class SQLObject
8
- def to_sql
9
- Mystic.adapter.serialize_sql self
10
- end
11
-
12
- alias_method :to_s, :to_sql
13
- end
14
-
15
- class Index < SQLObject
16
- attr_accessor :name, # Symbol or string
17
- :table_name, # Symbol or string
18
- :type, # Symbol
19
- :unique, # TrueClass/FalseClass
20
- :columns, # Array of Strings
21
- :opts # Hash, see below
22
-
23
- # opts
24
- # It's a Hash that represents options
25
- #
26
- # MYSQL ONLY
27
- # Key => Value (type)
28
- # :comment => A string that's up to 1024 chars (String)
29
- # :algorithm => The algorithm to use (Symbol)
30
- # :lock => The lock to use (Symbol)
31
- #
32
- # POSTGRES ONLY
33
- # Key => Value (type)
34
- # :fillfactor => A value in the range 10..100 (Integer)
35
- # :fastupdate => true/false (TrueClass/FalseClass)
36
- # :concurrently => true/false (TrueClass/FalseClass)
37
- # :tablespace => The name of the desired tablespace (String)
38
- # :buffering => :on/:off/:auto (Symbol)
39
- # :concurrently => true/false (TrueClass/FalseClass)
40
- # :where => The conditions for including entries in your index, same as SELECT * FROM table WHERE ____ (String)
41
-
42
- def initialize(opts={})
43
- opts.symbolize!
44
- raise ArgumentError, "Indeces need a table_name or else what's the point?." unless opts.member? :table_name
45
- raise ArgumentError, "Indeces need columns or else what's the point?" unless opts.member? :columns
46
- @name = opts.delete(:name).to_sym if opts.member? :name
47
- @table_name = opts.delete(:table_name).to_sym
48
- @type = (opts.delete :type || :btree).to_s.downcase.to_sym
49
- @unique = opts.delete :unique || false
50
- @columns = opts.delete(:columns).symbolize rescue []
51
- @opts = opts
52
- end
53
-
54
- # can accept shit other than columns like
55
- # box(location,location)
56
- def <<(col)
57
- case col
58
- when Column
59
- @columns << col.name.to_s
60
- when String
61
- @columns << col
62
- else
63
- raise ArgumentError, "Column must be a String or a Mystic::SQL::Column"
64
- end
65
- end
66
-
67
- alias_method :push, :<<
68
-
69
- def method_missing(meth, *args, &block)
70
- return @opts[meth] if @opts.member? meth
71
- nil
72
- end
73
- end
74
-
75
- class Column < SQLObject
76
- attr_accessor :name, :kind, :size, :constraints, :geom_kind, :geom_srid
77
-
78
- def initialize(opts={})
79
- @name = opts.delete(:name).to_s
80
- @kind = opts.delete(:kind).to_sym
81
- @size = opts.delete(:size).to_s if opts.member? :size
82
- @geom_kind = opts.delete(:geom_kind)
83
- @geom_srid = opts.delete(:geom_srid).to_i
84
- @constraints = opts
85
- end
86
-
87
- def geospatial?
88
- @geom_kind && @geom_srid
89
- end
90
- end
91
-
92
- class Table < SQLObject
93
- attr_reader :name
94
- attr_accessor :columns,
95
- :indeces,
96
- :operations,
97
- :opts
98
-
99
- def self.create(opts={})
100
- new true, opts
101
- end
102
-
103
- def self.alter(opts={})
104
- new false, opts
105
- end
106
-
107
- def initialize(is_create=true, opts={})
108
- @is_create = is_create
109
- @opts = opts.symbolize
110
- @columns = []
111
- @indeces = []
112
- @operations = []
113
-
114
- @name = @opts.delete(:name).to_s
115
- raise ArgumentError, "Argument 'name' is invalid." if @name.empty?
116
- end
117
-
118
- def create?
119
- @is_create
120
- end
121
-
122
- def <<(obj)
123
- case obj
124
- when Column
125
- @columns << obj
126
- when Index
127
- @indeces << obj
128
- when Operation
129
- @operations << obj
130
- else
131
- raise ArgumentError, "Argument is not a Mystic::SQL::Column, Mystic::SQL::Operation, or Mystic::SQL::Index."
132
- end
133
- end
134
-
135
- def to_sql
136
- raise ArgumentError, "Table cannot have zero columns." if @columns.empty?
137
- super
138
- end
139
-
140
- alias_method :push, :<<
141
-
142
- #
143
- ## Operation DSL
144
- #
145
-
146
- def drop_index(idx_name)
147
- raise Mystic::SQL::Error, "Cannot drop an index on a table that doesn't exist." if create?
148
- self << Mystic::SQL::Operation.drop_index(
149
- :index_name => idx_name.to_s,
150
- :table_name => self.name.to_s
151
- )
152
- end
153
-
154
- def rename_column(oldname, newname)
155
- raise Mystic::SQL::Error, "Cannot rename a column on a table that doesn't exist." if create?
156
- self << Mystic::SQL::Operation.rename_column(
157
- :table_name => self.name.to_s,
158
- :old_name => oldname.to_s,
159
- :new_name => newname.to_s
160
- )
161
- end
162
-
163
- def rename(newname)
164
- raise Mystic::SQL::Error, "Cannot rename a table that doesn't exist." if create?
165
- self << Mystic::SQL::Operation.rename_table(
166
- :old_name => self.name.dup.to_s,
167
- :new_name => newname.to_s,
168
- :callback => lambda { self.name = newname }
169
- )
170
- end
171
-
172
- def drop_columns(*col_names)
173
- raise Mystic::SQL::Error, "Cannot drop a column(s) on a table that doesn't exist." if create?
174
- self << Mystic::SQL::Operation.drop_columns(
175
- :table_name => self.name.to_s,
176
- :column_names => col_names.map(&:to_s)
177
- )
178
- end
179
-
180
- #
181
- ## Column DSL
182
- #
183
-
184
- def column(col_name, kind, opts={})
185
- self << Mystic::SQL::Column.new({
186
- :name => col_name,
187
- :kind => kind.to_sym
188
- }.merge(opts || {}))
189
- end
190
-
191
- def geometry(col_name, kind, srid, opts={})
192
- self << Mystic::SQL::Column.new({
193
- :name => col_name,
194
- :kind => :geometry,
195
- :geom_kind => kind,
196
- :geom_srid => srid
197
- }.merge(opts || {}))
198
- end
199
-
200
- def index(*cols)
201
- opts = cols.delete_at -1 if cols.last.is_a? Hash
202
- opts ||= {}
203
- opts[:columns] = cols
204
- opts[:table_name] = @name
205
- self << Mystic::SQL::Index.new(opts)
206
- end
207
-
208
- def method_missing(meth, *args, &block)
209
- return column args[0], meth.to_s, args[1] if args.count > 0
210
- return @opts[meth] if @opts.member?(meth)
211
- nil
212
- end
213
- end
214
-
215
- class Operation < SQLObject
216
- attr_reader :kind,
217
- :callback
218
-
219
- def initialize(kind, opts={})
220
- @kind = kind
221
- @opts = opts.dup
222
- @callback = @opts.delete :callback
223
- end
224
-
225
- def method_missing(meth, *args, &block)
226
- @opts[meth.to_s.to_sym] rescue nil
227
- end
228
-
229
- def self.method_missing(meth, *args, &block)
230
- new meth, (args[0] || {})
231
- end
232
- end
233
-
234
- class Raw < SQLObject
235
- def initialize(opts)
236
- @sql = opts[:sql]
237
- end
238
-
239
- def to_sql
240
- @sql
241
- end
242
- end
5
+ Error = Class.new StandardError
243
6
  end
244
- end
7
+ end
8
+
9
+ file_folder = File.dirname(File.absolute_path(__FILE__))
10
+ Dir.glob(file_folder + "/sql/**/*.rb", &method(:require))
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Mystic
4
+ module SQL
5
+ class Column
6
+ attr_accessor :name, :kind, :size, :constraints, :geom_kind, :geom_srid
7
+
8
+ def initialize(opts={})
9
+ @name = opts.delete(:name).to_s
10
+ @kind = opts.delete(:kind).to_sym
11
+ @size = opts.delete(:size).to_s if opts.member? :size
12
+ @geom_kind = opts.delete(:geom_kind)
13
+ @geom_srid = opts.delete(:geom_srid).to_i
14
+ @constraints = opts
15
+ end
16
+
17
+ def geospatial?
18
+ @geom_kind && @geom_srid
19
+ end
20
+
21
+ def to_s
22
+ sql = []
23
+ sql << name
24
+ sql << kind.downcase
25
+ sql << "(#{size})" if size && !size.empty? && !geospatial?
26
+ sql << "(#{geom_kind}, #{geom_srid})" if geospatial?
27
+ sql << (constraints[:null] ? "NULL" : "NOT NULL") if constraints.member?(:null)
28
+ sql << "UNIQUE" if constraints[:unique]
29
+ sql << "PRIMARY KEY" if constraints[:primary_key]
30
+ sql << "REFERENCES " + constraints[:references] if constraints.member?(:references)
31
+ sql << "DEFAULT " + constraints[:default] if constraints.member?(:default)
32
+ sql << "CHECK(#{constraints[:check]})" if constraints.member?(:check)
33
+ sql*" "
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Mystic
4
+ module SQL
5
+ class Index
6
+ attr_accessor :name, # Symbol or string
7
+ :table_name, # Symbol or string
8
+ :type, # Symbol
9
+ :unique, # TrueClass/FalseClass
10
+ :columns, # Array of Strings
11
+ :opts # Hash, see below
12
+
13
+ INDEX_TYPES = [
14
+ :btree,
15
+ :hash,
16
+ :gist,
17
+ :spgist,
18
+ :gin
19
+ ].freeze
20
+
21
+ # opts
22
+ # It's a Hash that represents options
23
+ #
24
+ # Key => Value (type)
25
+ # :fillfactor => A value in the range 10..100 (Integer)
26
+ # :fastupdate => true/false (TrueClass/FalseClass)
27
+ # :concurrently => true/false (TrueClass/FalseClass)
28
+ # :tablespace => The name of the desired tablespace (String)
29
+ # :buffering => :on/:off/:auto (Symbol)
30
+ # :concurrently => true/false (TrueClass/FalseClass)
31
+ # :where => The conditions for including entries in your index, same as SELECT * FROM table WHERE ____ (String)
32
+
33
+ def initialize opts={}
34
+ opts.symbolize!
35
+ raise ArgumentError, "Missing table_name." unless opts.member? :table_name
36
+ raise ArgumentError, "Indeces need columns or else what's the point?" unless opts.member? :columns
37
+ @name = opts.delete(:name).to_sym if opts.member? :name
38
+ @table_name = opts.delete(:table_name).to_sym
39
+ @type = (opts.delete(:type) || :btree).to_s.downcase.to_sym
40
+ @unique = opts.delete :unique || false
41
+ @columns = opts.delete(:columns).symbolize rescue []
42
+ @opts = opts
43
+ end
44
+
45
+ # can accept shit other than columns like
46
+ # box(location,location)
47
+ def << col
48
+ case col
49
+ when Column then @columns << col.name.to_s
50
+ when String then @columns << col
51
+ else raise ArgumentError, "Column must be a String or a Mystic::SQL::Column" end
52
+ end
53
+
54
+ def method_missing(meth, *args, &block)
55
+ return @opts[meth] if @opts.member? meth
56
+ nil
57
+ end
58
+
59
+ def to_s
60
+ storage_params = opts.subhash :fillfactor, :buffering, :fastupdate
61
+
62
+ sql = []
63
+ sql << "CREATE"
64
+ sql << "UNIQUE" if unique
65
+ sql << "INDEX"
66
+ sql << "CONCURENTLY" if concurrently
67
+ sql << name unless name.nil?
68
+ sql << "ON #{table_name}"
69
+ sql << "USING #{type}" if INDEX_TYPES.include? type
70
+ sql << "(#{columns.map(&:to_s).join ',' })"
71
+ sql << "WITH (#{storage_params.sqlize})" unless storage_params.empty?
72
+ sql << "TABLESPACE #{tablespace}" unless tablespace.nil?
73
+ sql << "WHERE #{where}" unless where.nil?
74
+ sql*' '
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Mystic
4
+ module SQL
5
+ class Table
6
+ attr_reader :name
7
+ attr_accessor :columns,
8
+ :indeces,
9
+ :operations,
10
+ :opts
11
+
12
+ def self.create opts={}
13
+ new true, opts
14
+ end
15
+
16
+ def self.alter opts={}
17
+ new false, opts
18
+ end
19
+
20
+ def initialize is_create=true, opts={}
21
+ @is_create = is_create
22
+ @opts = opts.symbolize
23
+ @columns = []
24
+ @indeces = []
25
+ @operations = []
26
+
27
+ @name = @opts.delete(:name).to_s
28
+ raise ArgumentError, "Argument 'name' is invalid." if @name.empty?
29
+ end
30
+
31
+ def create?
32
+ @is_create
33
+ end
34
+
35
+ def << obj
36
+ case obj
37
+ when Column then @columns << obj
38
+ when Index then @indeces << obj
39
+ when Operation then @operations << obj
40
+ when String then @operations << obj
41
+ else raise ArgumentError, "Argument is not a Mystic::SQL::Column, Mystic::SQL::Operation, or Mystic::SQL::Index." end
42
+ end
43
+
44
+ def to_s
45
+ raise ArgumentError, "Table cannot have zero columns." if @columns.empty?
46
+ sql = []
47
+
48
+ if create?
49
+ tbl = []
50
+ tbl << "CREATE TABLE #{name} (#{columns.map(&:to_s)*","})"
51
+ tbl << "INHERITS #{opts[:inherits]}" if opts[:inherits]
52
+ tbl << "TABLESPACE #{opts[:tablespace]}" if opts[:tablespace]
53
+ sql << tbl*' '
54
+ else
55
+ sql << "ALTER TABLE #{name} #{columns.map{ |c| "ADD COLUMN #{c.to_s}" }*', ' }"
56
+ end
57
+
58
+ sql.push(*indeces.map(&:to_s)) unless indeces.empty?
59
+ sql.push(*operations.map(&:to_s)) unless operations.empty?
60
+ sql*'; '
61
+ end
62
+
63
+ #
64
+ ## Operation DSL
65
+ #
66
+
67
+ def drop_index idx_name
68
+ raise Mystic::SQL::Error, "Cannot drop an index on a table that doesn't exist." if create?
69
+ self << "DROP INDEX #{idx_name}"
70
+ end
71
+
72
+ def rename_column oldname, newname
73
+ raise Mystic::SQL::Error, "Cannot rename a column on a table that doesn't exist." if create?
74
+ self << "ALTER TABLE #{table_name} RENAME COLUMN #{old_name} TO #{new_name}"
75
+ end
76
+
77
+ def rename newname
78
+ raise Mystic::SQL::Error, "Cannot rename a table that doesn't exist." if create?
79
+ self << "ALTER TABLE #{table_name} RENAME TO #{newname}"
80
+ self.name = newname
81
+ end
82
+
83
+ def drop_columns *col_names
84
+ raise Mystic::SQL::Error, "Cannot drop a column(s) on a table that doesn't exist." if create?
85
+ self << "ALTER TABLE #{table_name} #{col_names.map { |c| "DROP COLUMN #{c.to_s}" }*', ' }"
86
+ end
87
+
88
+ #
89
+ ## Column DSL
90
+ #
91
+
92
+ def column col_name, kind, opts={}
93
+ self << Mystic::SQL::Column.new({
94
+ :name => col_name,
95
+ :kind => kind.to_sym
96
+ }.merge(opts || {}))
97
+ end
98
+
99
+ def geometry col_name, kind, srid, opts={}
100
+ self << Mystic::SQL::Column.new({
101
+ :name => col_name,
102
+ :kind => :geometry,
103
+ :geom_kind => kind,
104
+ :geom_srid => srid
105
+ }.merge(opts || {}))
106
+ end
107
+
108
+ def index *cols
109
+ opts = cols.delete_at -1 if cols.last.is_a? Hash
110
+ opts ||= {}
111
+ opts[:columns] = cols
112
+ opts[:table_name] = @name
113
+ self << Mystic::SQL::Index.new(opts)
114
+ end
115
+
116
+ def method_missing meth, *args, &block
117
+ return column args[0], meth.to_s, args[1] if args.count > 0
118
+ super
119
+ end
120
+ end
121
+ end
122
+ end