ibruby 0.5.1-mswin32 → 0.5.2-mswin32

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.
Files changed (5) hide show
  1. data/lib/ib_lib.so +0 -0
  2. data/lib/ibmeta.rb +823 -0
  3. data/lib/ibruby.rb +1 -1
  4. metadata +4 -4
  5. data/lib/SQLType.rb +0 -230
data/lib/ib_lib.so CHANGED
Binary file
data/lib/ibmeta.rb ADDED
@@ -0,0 +1,823 @@
1
+ #-------------------------------------------------------------------------------
2
+ # ibmeta.rb
3
+ #-------------------------------------------------------------------------------
4
+ # Copyright � Peter Wood, 2005; Richard Vowles, 2006
5
+ #
6
+ # The contents of this file are subject to the Mozilla Public License Version
7
+ # 1.1 (the "License"); you may not use this file except in compliance with the
8
+ # License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.mozilla.org/MPL/
11
+ #
12
+ # Software distributed under the License is distributed on an "AS IS" basis,
13
+ # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
14
+ # the specificlanguage governing rights and limitations under the License.
15
+ #
16
+ # The Original Code is the FireRuby extension for the Ruby language.
17
+ #
18
+ # The Initial Developer of the Original Code is Peter Wood. All Rights
19
+ # Reserved. All modifications (and all InterBaseMetaFunctions) are by Richard Vowles
20
+ # (richard@developers-inc.co.nz).
21
+
22
+ module IBRuby
23
+ class InterBaseIndex
24
+
25
+ attr_accessor :table, :name, :unique, :columns, :direction, :active
26
+
27
+ def initialize(table, name, unique, columns, direction = nil, active = nil)
28
+ @table = table
29
+ @name = name
30
+ @unique = unique
31
+ @columns = columns
32
+ @direction = direction
33
+ @active = active
34
+ end
35
+
36
+ INDEX_ACTIVE = :INDEX_ACTIVE
37
+ INDEX_INACTIVE = :INDEX_INACTIVE
38
+
39
+ def to_sql
40
+ sql = "create"
41
+ if unique == true
42
+ sql << " unique"
43
+ end
44
+ if @direction
45
+ case @direction
46
+ when InterBaseMetaFunctions::ASCENDING
47
+ sql << " asc"
48
+ when InterBaseMetaFunctions::DESCENDING
49
+ sql << " desc"
50
+ end
51
+ end
52
+
53
+ sql << " index"
54
+
55
+ sql << " #{@name}"
56
+
57
+ sql << " on #{@table} ("
58
+ columns.each() do |col|
59
+ sql << ", " unless col == columns.first
60
+ sql << ((col.instance_of? InterBaseColumn) ? col.name : col.to_s)
61
+ end
62
+
63
+ sql << ")"
64
+
65
+ #puts "sql is #{sql}"
66
+
67
+ sql
68
+ end
69
+
70
+ def create_index(conn)
71
+ #puts #{to_sql} vs #{self.to_sql}"
72
+ conn.execute_immediate(self.to_sql)
73
+ end
74
+
75
+ def rename_index(conn, new_name)
76
+ old_name = @name
77
+ @name = new_name
78
+ begin
79
+ create(conn)
80
+ @name = old_name
81
+ remove(conn)
82
+ ensure
83
+ @name = old_name
84
+ end
85
+ end
86
+
87
+ def change_activation(conn, activation)
88
+ sql = "alter index #{@name} "
89
+ sql << (activation == InterBaseIndex::INDEX_ACTIVE) ? "active" : "inactive"
90
+ conn.execute_immediate( sql )
91
+ end
92
+
93
+ def remove_index(conn)
94
+ conn.execute_immediate( "DROP INDEX #{name}" )
95
+ end
96
+ end
97
+
98
+ class InterBaseConstraint
99
+ attr_accessor :name, :type, :columns, :table, :foreign_cols, :foreign_table_name
100
+
101
+ PRIMARY_KEY = :PRIMARY_KEY
102
+ FOREIGN_KEY = :FOREIGN_KEY
103
+
104
+ # columns are strings only, not complete columns. if they are complete columns
105
+ # then we just get their name
106
+ def initialize(type, columns, foreign_cols=nil, foreign_table_name=nil, name=nil)
107
+ @name = name
108
+ @type = type
109
+ @columns = columns
110
+ @foreign_cols=foreign_cols
111
+ @foreign_table_name=foreign_table_name
112
+
113
+ #puts "constraint created #{to_sql}"
114
+ end
115
+
116
+ def to_sql
117
+ tab_name = table.nil? ? "NULL" : table.name
118
+ sql = "alter table #{tab_name} add "
119
+
120
+ if @type == :PRIMARY_KEY
121
+ sql << "primary key ("
122
+ sql << cycle_cols
123
+ sql << ")"
124
+ elsif @type == :FOREIGN_KEY
125
+ sql << "foreign key ("
126
+ sql << cycle_cols
127
+ sql << ") references #{foreign_table_name} ("
128
+ sql << cycle_cols(foreign_cols)
129
+ sql << ")"
130
+ else
131
+ raise NotImplementedError, "Other constraints #{@type.to_s} not implemented yet"
132
+ end
133
+ end
134
+
135
+ private
136
+ def cycle_cols(columns=@columns)
137
+ sql = ""
138
+ if !columns.nil?
139
+ col_count = 0
140
+ columns.each() do |col|
141
+ sql << ", " unless col_count == 0
142
+ col_count += 1 # can't use columns.first as it may not be passed first by each????
143
+ if col === InterBaseColumn
144
+ sql << col.name
145
+ else
146
+ sql << col.to_s
147
+ end
148
+ end
149
+ end
150
+
151
+ sql
152
+ end
153
+
154
+ end
155
+
156
+ class InterBaseTable
157
+ attr_accessor :name, :columns, :indices, :constraints
158
+
159
+ SQL_ALL = :SQL_ALL
160
+ SQL_TABLE = :SQL_TABLE
161
+ SQL_INDICES = :SQL_INDICES
162
+ SQL_PRIMARY_KEYS = :SQL_PRIMARY_KEYS
163
+ SQL_FOREIGN_KEYS = :SQL_FOREIGN_KEYS
164
+
165
+ def initialize(name, columns=[], indices=[], constraints=[] )
166
+ #puts "table name new table: #{name}"
167
+ @name = name.to_s.upcase
168
+ @columns = columns
169
+ @indices = indices
170
+ @constraints = constraints
171
+
172
+ if @constraints
173
+ @constraints.each() {|c| c.table = self }
174
+ end
175
+ end
176
+
177
+ def load(conn)
178
+ @columns = InterBaseMetaFunctions.table_fields(conn,@name,true)
179
+ @indices = InterBaseMetaFunctions.indices(conn,@name)
180
+ @constraints = InterBaseMetaFunctions.table_constraints(conn,@name)
181
+
182
+ #puts "#{@constraints.size} constraints found"
183
+
184
+ @constraints.each() {|c| c.table = self }
185
+
186
+ @loaded = true
187
+ end
188
+
189
+ def drop_table(conn)
190
+ #puts "DROP TABLE #{name}"
191
+ conn.execute_immediate( "DROP TABLE #{name}" )
192
+ end
193
+
194
+ def create_table(conn)
195
+ to_sql.each() do |sql|
196
+ #puts "executing: #{sql}"
197
+ conn.execute_immediate( sql )
198
+ end
199
+ end
200
+
201
+ ## returns an array of sql required to create the table and all dependents
202
+ # when reconstructing the database, create all the tables, then create the primary keys and then
203
+ # create the foreign keys and then the indices
204
+ def to_sql(sql_restriction=:SQL_ALL)
205
+ sql = []
206
+
207
+ if ( [:SQL_ALL, :SQL_TABLE].include?(sql_restriction) )
208
+ sql << to_sql_create_table
209
+ end
210
+
211
+ if ( [:SQL_ALL, :SQL_INDICES].include?(sql_restriction) && @indices )
212
+ @indices.each() {|index| sql << index.to_sql }
213
+ end
214
+
215
+ if ( [:SQL_ALL, :SQL_PRIMARY_KEYS].include?(sql_restriction) && @constraints )
216
+ @constraints.each() do |c|
217
+ if (c.type == InterBaseConstraint::PRIMARY_KEY)
218
+ sql << c.to_sql
219
+ end
220
+ end
221
+ end
222
+
223
+ if ( [:SQL_ALL, :SQL_FOREIGN_KEYS].include?(sql_restriction) && @constraints )
224
+ @constraints.each() do |c|
225
+ if (c.type == InterBaseConstraint::FOREIGN_KEY)
226
+ sql << c.to_sql
227
+ end
228
+ end
229
+ end
230
+
231
+ sql
232
+ end
233
+
234
+ def rename_table(conn, ntable_name)
235
+ new_table_name = ntable_name.to_s.upcase
236
+
237
+ if @loaded.nil? or !@loaded
238
+ load(conn)
239
+ end
240
+
241
+ old_table_name = @name
242
+ load(conn) # load the definition
243
+ @name = new_table_name
244
+ to_sql(:SQL_TABLE).each() {|sql| conn.execute_immediate(sql) }
245
+ # copy all the data across
246
+ conn.execute_immediate( "insert into #{new_table_name} select * from #{old_table_name}")
247
+ to_sql(:SQL_PRIMARY_KEYS).each() {|sql| conn.execute_immediate(sql) }
248
+ to_sql(:SQL_FOREIGN_KEYS).each() {|sql| conn.execute_immediate(sql) }
249
+ @indices.each() do |index|
250
+ index.remove_index(conn)
251
+ index.table = new_table_name
252
+ index.create_index(conn)
253
+ end
254
+ @name = old_table_name
255
+ drop_table(conn)
256
+ end
257
+
258
+ private
259
+ def to_sql_create_table
260
+ sql = "create table #{name} ("
261
+
262
+ if !columns.nil?
263
+ col_count = 0
264
+ columns.each() do |col|
265
+ sql << ", " unless col_count == 0
266
+ col_count += 1
267
+ sql << col.to_sql
268
+ end
269
+ end
270
+
271
+ sql << ")"
272
+
273
+ #puts sql
274
+ sql
275
+ end
276
+ end
277
+
278
+ # InterBaseMetaFunctions
279
+ # Rather than requiring Ruby on Rails for access to these useful functions, I decided to move
280
+ # them here instead.
281
+ #
282
+
283
+ class InterBaseMetaFunctions
284
+
285
+ ASCENDING = :ASCENDING
286
+ DESCENDING = :DESCENDING
287
+
288
+ def self.quote( value, column_meta_data )
289
+ if column_meta_data.expects_quoting
290
+ '#{value}'
291
+ elsif ((column_meta_data.type == InterBaseColumn::BLOB) && (column_meta_data.sub_type != 1 ) )
292
+ raise IBRubyException.new("'#{value}' is not a valid default for this column #{column_meta_data.name}.")
293
+ else
294
+ value
295
+ end
296
+ end
297
+
298
+
299
+ def self.table_names(conn)
300
+ tables = []
301
+
302
+ conn.execute_immediate("select rdb$relation_name from rdb$relations where rdb$relation_name not starting 'RDB$' and rdb$flags=1") do |row|
303
+ tables << row[0].to_s.rstrip
304
+ end
305
+
306
+ tables
307
+ end
308
+
309
+ def self.indices(conn, table_name)
310
+ indices = []
311
+
312
+ # we must make sure the current transaction is committed, otherwise this won't work!
313
+ indicesSQL = <<-END_SQL
314
+ select rdb$index_name, rdb$unique_flag,RDB$INDEX_INACTIVE,RDB$INDEX_TYPE from rdb$indices
315
+ where rdb$relation_name = '#{table_name.to_s.upcase}' and rdb$index_name not starting 'RDB$'
316
+ END_SQL
317
+
318
+ #~ puts "SQL"
319
+ #~ puts indicesSQL
320
+
321
+ conn.execute_immediate(indicesSQL) do |row|
322
+ #puts "index #{ib_to_ar_case(row[0].to_s.rstrip)}"
323
+ indices << InterBaseIndex.new(table_name, row[0].to_s.rstrip,
324
+ row[1].to_i == 1, [],
325
+ (row[3] == 1)?InterBaseMetaFunctions::DESCENDING : InterBaseMetaFunctions::ASCENDING,
326
+ (row[2] == 1)?InterBaseIndex::INDEX_INACTIVE : InterBaseIndex::INDEX_ACTIVE)
327
+ end
328
+
329
+ #puts "Indices size #{indices.size}"
330
+
331
+ if !indices.empty?
332
+ indices.each() do |index|
333
+ sql = "select rdb$field_name from rdb$index_segments where rdb$index_name "\
334
+ "= '#{index.name.upcase}' order by rdb$index_name, rdb$field_position"
335
+
336
+ #puts "index SQL: #{sql}"
337
+
338
+ conn.execute_immediate(sql) do |row|
339
+ index.columns << table_fields(conn, table_name, true, row[0].to_s.rstrip )
340
+ end # each row in the index and get the InterBaseColumn
341
+ end # each index
342
+ end # if we found indices
343
+
344
+ indices
345
+ end
346
+
347
+ def self.remove_index(conn, index_name)
348
+ conn.execute_immediate( "DROP INDEX #{index_name}" )
349
+ end
350
+
351
+ # This class method fetches the type details for a named table. The
352
+ # method returns a hash that links column names to InterBaseColumn objects.
353
+ #
354
+ # ==== Parameters
355
+ # table:: A string containing the name of the table.
356
+ # connection:: A reference to the connection to be used to determine
357
+ # the type information.
358
+ # extract_ordered: if true then returns an ordered array of columns otherwise returns hash with col names
359
+ # column_name: if true then returns a single column (not array or hash)
360
+ #
361
+ # ==== Exception
362
+ # IBRubyException:: Generated if an invalid table name is specified
363
+ # or an SQL error occurs.
364
+ def self.table_fields(connection, table, extract_ordered = false, column_name = nil)
365
+ # Check for naughty table names.
366
+ if /\s+/ =~ table.to_s
367
+ raise IBRubyException.new("'#{table.to_s}' is not a valid table name.")
368
+ end
369
+
370
+ extract_ordered = true if !column_name.nil?
371
+
372
+ types = extract_ordered ? [] : {}
373
+ begin
374
+ # r.rdb$field_source,
375
+ sql = "SELECT r.rdb$field_name, f.rdb$field_type, "\
376
+ "f.rdb$field_length, f.rdb$field_precision, f.rdb$field_scale * -1, "\
377
+ "f.rdb$field_sub_type, "\
378
+ "COALESCE(r.rdb$default_source, f.rdb$default_source) rdb$default_source, "\
379
+ "COALESCE(r.rdb$null_flag, f.rdb$null_flag) rdb$null_flag, rdb$character_length "\
380
+ "FROM rdb$relation_fields r "\
381
+ "JOIN rdb$fields f ON r.rdb$field_source = f.rdb$field_name "\
382
+ "WHERE r.rdb$relation_name = '#{table.to_s.upcase}'";
383
+
384
+ if !column_name.nil?
385
+ sql << " AND r.rdb$field_name = '#{column_name.to_s.upcase}'"
386
+ elsif extract_ordered
387
+ sql << " ORDER BY r.rdb$field_position"
388
+ end
389
+
390
+ #puts "sql: #{sql}"
391
+
392
+ # sql = "SELECT RF.RDB$FIELD_NAME, F.RDB$FIELD_TYPE, "\
393
+ # "F.RDB$FIELD_LENGTH, F.RDB$FIELD_PRECISION, "\
394
+ # "F.RDB$FIELD_SCALE * -1, F.RDB$FIELD_SUB_TYPE "\
395
+ # "FROM RDB$RELATION_FIELDS RF, RDB$FIELDS F "\
396
+ # "WHERE RF.RDB$RELATION_NAME = UPPER('#{table}') "\
397
+ # "AND RF.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME"
398
+
399
+ #connection.start_transaction do |tx|
400
+ # tx.execute(sql)
401
+ connection.execute_immediate(sql) do |row|
402
+ sql_type = InterBaseColumn.to_base_type(row[1], row[5])
403
+ type = nil
404
+ field_name = row[0].strip
405
+
406
+ #column_name, table_name, type, default_source=nil, null_flag=false, length=nil, precision=nil, scale=nil, sub_type=nil )
407
+ #row[0], row
408
+ case sql_type
409
+ when InterBaseColumn::BLOB
410
+ type = InterBaseColumn.new(field_name, table.to_s, sql_type, row[6], !row[7].nil?, nil, nil, nil, row[5] )
411
+ when InterBaseColumn::CHAR, InterBaseColumn::VARCHAR
412
+ type = InterBaseColumn.new(field_name, table.to_s, sql_type, row[6], !row[7].nil?, row[8] )
413
+ # row[8] is the real length, field_length depends on the character set being used
414
+ when InterBaseColumn::DECIMAL, InterBaseColumn::NUMERIC
415
+ type = InterBaseColumn.new(field_name, table.to_s, sql_type, row[6], !row[7].nil?, nil, row[3], row[4] )
416
+ else
417
+ type = InterBaseColumn.new(field_name, table.to_s, sql_type, row[6], !row[7].nil? )
418
+ end
419
+
420
+ if extract_ordered
421
+ types << type
422
+ else
423
+ types[field_name] = type
424
+ end
425
+
426
+ #puts "col: #{type.to_sql}"
427
+ end
428
+
429
+ #end
430
+ end
431
+ if ( types.size > 1 || column_name.nil? )
432
+ types
433
+ elsif (types.size == 1)
434
+ types[0]
435
+ else
436
+ nil
437
+ end
438
+ end # table_fields
439
+
440
+ def self.table_constraints(conn, table_name)
441
+ sql = "select rdb$constraint_name, rdb$constraint_type, rdb$index_name "\
442
+ "from rdb$relation_constraints "\
443
+ "where rdb$constraint_type in ('FOREIGN KEY', 'PRIMARY KEY' ) "\
444
+ "and rdb$relation_name = '#{table_name.to_s.upcase}'"
445
+
446
+ constraints = []
447
+
448
+ conn.execute_immediate(sql) do |constraint|
449
+ constraint_name = constraint[0].strip
450
+ constraint_type = ( constraint[1].strip == 'PRIMARY KEY' ) ?
451
+ InterBaseConstraint::PRIMARY_KEY : InterBaseConstraint::FOREIGN_KEY;
452
+ representing_index = constraint[2]
453
+ # now we need to get the columns that are being keyed on on this table, that is the same
454
+ # for PK and FK
455
+ columns = []
456
+ conn.execute_immediate( "select rdb$field_name "\
457
+ "from rdb$index_segments where rdb$index_name='#{representing_index}'") do |col|
458
+ columns << col[0].to_s.strip
459
+ end
460
+ # and now for foreign keys, we need to find out what the foreign key index name is
461
+ if constraint_type == InterBaseConstraint::FOREIGN_KEY
462
+ fk_columns = []
463
+ foreign_key_index = nil
464
+ foreign_key_table = nil
465
+ conn.execute_immediate( "select rdb$foreign_key from rdb$indices "\
466
+ "where rdb$index_name='#{representing_index}'") do |fk|
467
+ foreign_key_index = fk[0].strip
468
+ end
469
+ conn.execute_immediate( "select rdb$relation_name from rdb$indices "\
470
+ "where rdb$index_name='#{foreign_key_index}'") do |fk|
471
+ foreign_key_table = fk[0].strip
472
+ end
473
+ conn.execute_immediate( "select rdb$field_name "\
474
+ "from rdb$index_segments where rdb$index_name='#{foreign_key_index}'") do |col|
475
+ fk_columns << col[0].to_s.strip
476
+ end
477
+
478
+ constraints << InterBaseConstraint.new( constraint_type, columns, fk_columns, foreign_key_table )
479
+ else
480
+ constraints << InterBaseConstraint.new( constraint_type, columns )
481
+ end #if constraints_type
482
+ end #conn.execute_immediate
483
+
484
+ constraints
485
+ end #def table_constraints
486
+
487
+ def self.db_type_cast( conn, column_type, column_value )
488
+ sql = "SELECT CAST(#{column_value} AS #{column_type}) FROM RDB$DATABASE ROWS 1 TO 1"
489
+ #puts "db_type_cast: #{sql}"
490
+ retVal = nil
491
+ conn.execute_immediate(sql) do |row|
492
+ retVal = row[0]
493
+ end
494
+ retVal
495
+ end
496
+
497
+
498
+ # end of InterBaseMetaFunctions class
499
+ end
500
+
501
+
502
+ # This class is used to represent SQL table column tables.
503
+ class InterBaseColumn
504
+ # allow these to be overriden
505
+ @@default_precision = 10
506
+ @@default_scale = 2
507
+ @@default_length = 255
508
+
509
+ # A definition for a base SQL type.
510
+ BOOLEAN = :BOOLEAN
511
+
512
+ # A definition for a base SQL type.
513
+ BLOB = :BLOB
514
+
515
+ # A definition for a base SQL type.
516
+ CHAR = :CHAR
517
+
518
+ # A definition for a base SQL type.
519
+ DATE = :DATE
520
+
521
+ # A definition for a base SQL type.
522
+ DECIMAL = :DECIMAL
523
+
524
+ # A definition for a base SQL type.
525
+ DOUBLE = :DOUBLE
526
+
527
+ # A definition for a base SQL type.
528
+ FLOAT = :FLOAT
529
+
530
+ # A definition for a base SQL type.
531
+ INTEGER = :INTEGER
532
+
533
+ # A definition for a base SQL type.
534
+ NUMERIC = :NUMERIC
535
+
536
+ # A definition for a base SQL type.
537
+ SMALLINT = :SMALLINT
538
+
539
+ # A definition for a base SQL type.
540
+ TIME = :TIME
541
+
542
+ # A definition for a base SQL type.
543
+ TIMESTAMP = :TIMESTAMP
544
+
545
+ # A definition for a base SQL type.
546
+ VARCHAR = :VARCHAR
547
+
548
+ # data type can be returned when arithmetic occurs, e.g. SMALLINT * -1 returns a INT64
549
+ INT64 = :INT64
550
+
551
+ # Attribute accessor.
552
+ attr_accessor :type, :length, :precision, :scale, :sub_type, :name, :table_name, :default, :not_null
553
+
554
+ def self.expects_quoting(col)
555
+ case col.type
556
+ when InterBaseColumn::NUMERIC, InterBaseColumn::DECIMAL, InterBaseColumn::INTEGER,
557
+ InterBaseColumn::DOUBLE, InterBaseColumn::FLOAT
558
+ false
559
+ when InterBaseColumn::CHAR, InterBaseColumn::VARCHAR, InterBaseColumn::BLOB,
560
+ InterBaseColumn::DATE, InterBaseColumn::TIME, InterBaseColumn::TIMESTAMP
561
+ true
562
+ else
563
+ nil
564
+ end
565
+ end
566
+
567
+ def expects_quoting
568
+ InterBaseColumn.expects_quoting(self)
569
+ end
570
+
571
+ # This is the constructor for the InterBaseColumn class.
572
+ #
573
+ # ==== Parameters
574
+ # type:: The base type for the InterBaseColumn object. Must be one of the
575
+ # base types defined within the class.
576
+ # length:: The length setting for the type. Defaults to nil.
577
+ # precision:: The precision setting for the type. Defaults to nil.
578
+ # scale:: The scale setting for the type. Defaults to nil.
579
+ # sub_type:: The SQL sub-type setting. Defaults to nil.
580
+ # default_source:: the whole string "default xxx" or "default 'xxx'"
581
+ # not_null: true for NOT NULL, false for nulls allowed
582
+ # actual_default:: if specified then we don't bother processing default_source
583
+ def initialize(column_name, table_name, type, default_source=nil, not_null=false,
584
+ length=nil, precision=10, scale=0, sub_type=nil, actual_default=nil )
585
+ @name = column_name
586
+ @table_name = table_name
587
+ @not_null = not_null
588
+ @type = type
589
+ @length = length
590
+ @precision = precision
591
+ @scale = scale
592
+ @sub_type = sub_type
593
+
594
+
595
+ if !actual_default.nil?
596
+ #puts "actual default #{actual_default}"
597
+ @default = actual_default
598
+ else
599
+ validate_default_source(default_source)
600
+ end
601
+ end
602
+
603
+ def validate
604
+ # ensure sensible defaults are set
605
+ @precision = @@default_precision if @precision.nil?
606
+ @scale = @@default_scale if @scale.nil?
607
+ @length = @@default_length if @length.nil?
608
+ end
609
+
610
+ def validate_default_source(default_source)
611
+ if !default_source.nil?
612
+ #puts "checking default: #{default_source}"
613
+ match = Regexp.new( '^\s*DEFAULT\s+(.*)\s*', Regexp::IGNORECASE )
614
+ matchData = match.match(default_source.to_s)
615
+ if matchData
616
+ @default = matchData[1]
617
+
618
+ #puts "result was #{@default} type is #{@type}"
619
+
620
+ if @default
621
+ if InterBaseColumn.expects_quoting(self)
622
+ len = @default.size - 2
623
+ @default = @default[1..len]
624
+ else
625
+ case @type
626
+ when InterBaseColumn::BOOLEAN
627
+ @default = "true".casecmp( @default.to_s ) == 0
628
+ when InterBaseColumn::DECIMAL, InterBaseColumn::NUMERIC
629
+ @default = BigDecimal.new( @default.to_s )
630
+ when InterBaseColumn::DOUBLE, InterBaseColumn::FLOAT
631
+ @default = @default.to_f
632
+ when InterBaseColumn::INTEGER
633
+ @default = @default.to_i
634
+ when InterBaseColumn::DATE, InterBaseColumn::TIME, InterBaseColumn::TIMESTAMP
635
+ if @default.to_s !~ /^current/i
636
+ @default = InterBaseMetaFunctions.db_type_cast( @default, to_s )
637
+ end
638
+ end
639
+ end
640
+ end
641
+ end
642
+ else
643
+ #puts "default source passed is null"
644
+ @default = nil
645
+ end
646
+ end
647
+
648
+
649
+ # This method overloads the equivalence test operator for the InterBaseColumn
650
+ # class.
651
+ #
652
+ # ==== Parameters
653
+ # object:: A reference to the object to be compared with.
654
+ def ==(object)
655
+ result = false
656
+ if object.instance_of?(InterBaseColumn)
657
+ result = (@type == object.type &&
658
+ @length == object.length &&
659
+ @precision == object.precision &&
660
+ @scale == object.scale &&
661
+ @sub_type == object.sub_type)
662
+ end
663
+ result
664
+ end
665
+
666
+
667
+ # This method generates a textual description for a InterBaseColumn object.
668
+ def to_s
669
+ validate # ensure sensible defaults
670
+
671
+ if @type == InterBaseColumn::DECIMAL or @type == InterBaseColumn::NUMERIC
672
+ "#{@type.id2name}(#{@precision},#{@scale})"
673
+ elsif @type == InterBaseColumn::BLOB
674
+ "#{@type.id2name} SUB_TYPE #{@sub_type}"
675
+ elsif @type == InterBaseColumn::CHAR or @type == InterBaseColumn::VARCHAR
676
+ "#{@type.id2name}(#{@length})"
677
+ elsif @type == InterBaseColumn::DOUBLE
678
+ "DOUBLE PRECISION"
679
+ else
680
+ @type.id2name
681
+ end
682
+ end
683
+
684
+ def to_sql
685
+ sql = name + " " + to_s
686
+ if !@default.nil?
687
+ sql << " default "
688
+ equote = expects_quoting
689
+ sql << "'" if equote
690
+ sql << @default.to_s
691
+ sql << "'" if equote
692
+ end
693
+ if @not_null == true
694
+ sql << " not null"
695
+ end
696
+ sql
697
+ # all manner of other things, we are ignoring (e.g. check constraints)
698
+ end
699
+
700
+ def to_base_type
701
+ InterBaseColumn.to_base_type(self.type, self.sub_type)
702
+ end
703
+
704
+ # This class method converts a InterBase internal type to a InterBaseColumn base
705
+ # type.
706
+ #
707
+ # ==== Parameters
708
+ # type:: A reference to the Interbase field type value.
709
+ # sub_type:: A reference to the Interbase field subtype value.
710
+ def self.to_base_type(type, subtype)
711
+ case type
712
+ when 16 # DECIMAL, NUMERIC
713
+ if subtype
714
+ subtype == 1 ? InterBaseColumn::NUMERIC : InterBaseColumn::DECIMAL
715
+ else
716
+ InterBaseColumn::INT64 # can't actually define a column of this type
717
+ end
718
+ when 17 # BOOLEAN
719
+ InterBaseColumn::BOOLEAN
720
+
721
+ when 261 # BLOB
722
+ InterBaseColumn::BLOB
723
+
724
+ when 14 # CHAR
725
+ InterBaseColumn::CHAR
726
+
727
+ when 12 # DATE
728
+ InterBaseColumn::DATE
729
+
730
+ when 27 # DOUBLE, DECIMAL, NUMERIC
731
+ if subtype
732
+ subtype == 1 ? InterBaseColumn::NUMERIC : InterBaseColumn::DECIMAL
733
+ else
734
+ InterBaseColumn::DOUBLE
735
+ end
736
+
737
+ when 10 # FLOAT
738
+ InterBaseColumn::FLOAT
739
+
740
+ when 8 # INTEGER, DECIMAL, NUMERIC
741
+ if subtype
742
+ subtype == 1 ? InterBaseColumn::NUMERIC : InterBaseColumn::DECIMAL
743
+ else
744
+ InterBaseColumn::INTEGER
745
+ end
746
+
747
+ when 7 # SMALLINT, DECIMAL, NUMERIC
748
+ if subtype
749
+ subtype == 1 ? InterBaseColumn::NUMERIC : InterBaseColumn::DECIMAL
750
+ else
751
+ InterBaseColumn::SMALLINT
752
+ end
753
+
754
+ when 13 # TIME
755
+ InterBaseColumn::TIME
756
+
757
+ when 35 # TIMESTAMP
758
+ InterBaseColumn::TIMESTAMP
759
+
760
+ when 37 # VARCHAR
761
+ InterBaseColumn::VARCHAR
762
+ end
763
+ end
764
+
765
+ # we should also check to see if this table has indexes which need to be dropped and re-created
766
+ # but the migrations user should really do that
767
+ def rename_column( connection, new_column_name )
768
+ #puts "alter table #{@table_name} alter column #{@name} to #{new_column_name}"
769
+ connection.execute_immediate( "alter table #{@table_name} alter column #{@name} to #{new_column_name}" )
770
+ end
771
+
772
+ # this column does not exist in the database, please create it!
773
+ def add_column( connection )
774
+ validate # ensure sensible defaults
775
+ #puts "alter table #{@table_name} add #{self.to_sql}"
776
+ connection.execute_immediate( "alter table #{@table_name} add #{self.to_sql}" )
777
+ end
778
+
779
+ def change_column(conn, new_column)
780
+ new_column.validate # ensure sensible defaults
781
+
782
+ if new_column.type != self # should use == defined above
783
+ change_type_sql = "ALTER TABLE #{@table_name} alter #{@name} type #{new_column.to_s}"
784
+ #puts change_type_sql
785
+ conn.execute_immediate(change_type_sql)
786
+ end
787
+
788
+ if new_column.not_null != @not_null
789
+ # now change the NULL status, this may make the table invalid so...
790
+ nullFlag = new_column.not_null ? "1" : "NULL"
791
+ update_relations_fields_sql =
792
+ "UPDATE rdb$relation_fields set rdb$null_flags = #{nullFlag}"\
793
+ " where rdb$relation_name='#{@table_name.upcase}' and "\
794
+ "rdb$field_name='#{@name.upcase}'"
795
+ conn.execute_immediate(update_relations_fields_sql)
796
+ end
797
+
798
+ # changed default or changed type
799
+ if (new_column.default != @default) || (new_column.type != self)
800
+ # now the default change, which is complicated!
801
+ defaultSource = new_column.default.nil? ? "" : ("default " << InterBaseMetaFunctions.quote(new_column.default.to_s, new_column ) )
802
+ #puts "alter table #{@table_name} add ib$$temp type #{new_column.to_s} #{defaultSource}"
803
+ conn.execute_immediate("alter table #{@table_name} add ib$$temp #{new_column.to_s} #{defaultSource}")
804
+
805
+ # standard hack to change the default type
806
+ begin
807
+ sql = <<-END_SQL
808
+ update rdb$relation_fields set
809
+ rdb$default_source=(select rdb$default_source from rdb$relation_fields where
810
+ rdb$field_name='IB$$TEMP' and rdb$relation_name='#{@table_name.upcase}'),
811
+ rdb$default_value=(select rdb$default_value from rdb$relation_fields where
812
+ rdb$field_name='IB$$TEMP' and rdb$relation_name='#{@table_name.upcase}')
813
+ where rdb$field_name='#{@name.upcase}' and rdb$relation_name='#{@table_name.upcase}';
814
+ END_SQL
815
+ conn.execute_immediate(sql)
816
+ ensure
817
+ conn.execute_immediate("alter table #{@table_name} drop ib$$temp" )
818
+ end
819
+ end
820
+ end
821
+
822
+ end # End of the InterBaseColumn class.
823
+ end # End of the IBRuby module.
data/lib/ibruby.rb CHANGED
@@ -19,4 +19,4 @@
19
19
  # Reserved.
20
20
 
21
21
  require 'ib_lib'
22
- require 'SQLType'
22
+ require 'ibmeta'
metadata CHANGED
@@ -3,12 +3,12 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: ibruby
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.5.1
7
- date: 2006-12-05 00:00:00 +13:00
6
+ version: 0.5.2
7
+ date: 2006-12-23 00:00:00 +13:00
8
8
  summary: Ruby interface library for the InterBase database.
9
9
  require_paths:
10
10
  - lib
11
- email: paw220470@yahoo.ie, rvowles@codegear.com
11
+ email: paw220470@yahoo.ie, richard@developers-inc.co.nz
12
12
  homepage: http://rubyforge.org/projects/ibruby/
13
13
  rubyforge_project:
14
14
  description: IBRuby is an extension to the Ruby programming language that provides access to the InterBase RDBMS. IBRuby is based in the InterBase C API and has no additional dependencies. The IBRuby library wraps the API calls in a Ruby OO interface.
@@ -29,10 +29,10 @@ post_install_message:
29
29
  authors:
30
30
  - Peter Wood, Richard Vowles
31
31
  files:
32
+ - lib/ibmeta.rb
32
33
  - lib/ibruby.rb
33
34
  - lib/ib_lib.so
34
35
  - lib/mkdoc
35
- - lib/SQLType.rb
36
36
  - lib/src.rb
37
37
  - test/AddRemoveUserTest.rb
38
38
  - test/BackupRestoreTest.rb
data/lib/SQLType.rb DELETED
@@ -1,230 +0,0 @@
1
- #-------------------------------------------------------------------------------
2
- # SQLType.rb
3
- #-------------------------------------------------------------------------------
4
- # Copyright � Peter Wood, 2005; Richard Vowles, 2006
5
- #
6
- # The contents of this file are subject to the Mozilla Public License Version
7
- # 1.1 (the "License"); you may not use this file except in compliance with the
8
- # License. You may obtain a copy of the License at
9
- #
10
- # http://www.mozilla.org/MPL/
11
- #
12
- # Software distributed under the License is distributed on an "AS IS" basis,
13
- # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
14
- # the specificlanguage governing rights and limitations under the License.
15
- #
16
- # The Original Code is the FireRuby extension for the Ruby language.
17
- #
18
- # The Initial Developer of the Original Code is Peter Wood. All Rights
19
- # Reserved.
20
-
21
- module IBRuby
22
- # This class is used to represent SQL table column tables.
23
- class SQLType
24
- # A definition for a base SQL type.
25
- BOOLEAN = :BOOLEAN
26
-
27
- # A definition for a base SQL type.
28
- BLOB = :BLOB
29
-
30
- # A definition for a base SQL type.
31
- CHAR = :CHAR
32
-
33
- # A definition for a base SQL type.
34
- DATE = :DATE
35
-
36
- # A definition for a base SQL type.
37
- DECIMAL = :DECIMAL
38
-
39
- # A definition for a base SQL type.
40
- DOUBLE = :DOUBLE
41
-
42
- # A definition for a base SQL type.
43
- FLOAT = :FLOAT
44
-
45
- # A definition for a base SQL type.
46
- INTEGER = :INTEGER
47
-
48
- # A definition for a base SQL type.
49
- NUMERIC = :NUMERIC
50
-
51
- # A definition for a base SQL type.
52
- SMALLINT = :SMALLINT
53
-
54
- # A definition for a base SQL type.
55
- TIME = :TIME
56
-
57
- # A definition for a base SQL type.
58
- TIMESTAMP = :TIMESTAMP
59
-
60
- # A definition for a base SQL type.
61
- VARCHAR = :VARCHAR
62
-
63
- # Attribute accessor.
64
- attr_reader :type, :length, :precision, :scale, :subtype
65
-
66
-
67
- # This is the constructor for the SQLType class.
68
- #
69
- # ==== Parameters
70
- # type:: The base type for the SQLType object. Must be one of the
71
- # base types defined within the class.
72
- # length:: The length setting for the type. Defaults to nil.
73
- # precision:: The precision setting for the type. Defaults to nil.
74
- # scale:: The scale setting for the type. Defaults to nil.
75
- # subtype:: The SQL sub-type setting. Defaults to nil.
76
- def initialize(type, length=nil, precision=nil, scale=nil, subtype=nil)
77
- @type = type
78
- @length = length
79
- @precision = precision
80
- @scale = scale
81
- @subtype = subtype
82
- end
83
-
84
-
85
- # This class method fetches the type details for a named table. The
86
- # method returns a hash that links column names to SQLType objects.
87
- #
88
- # ==== Parameters
89
- # table:: A string containing the name of the table.
90
- # connection:: A reference to the connection to be used to determine
91
- # the type information.
92
- #
93
- # ==== Exception
94
- # IBRubyException:: Generated if an invalid table name is specified
95
- # or an SQL error occurs.
96
- def SQLType.for_table(table, connection)
97
- # Check for naughty table names.
98
- if /\s+/ =~ table
99
- raise IBRubyException.new("'#{table}' is not a valid table name.")
100
- end
101
-
102
- types = {}
103
- begin
104
- sql = "SELECT RF.RDB$FIELD_NAME, F.RDB$FIELD_TYPE, "\
105
- "F.RDB$FIELD_LENGTH, F.RDB$FIELD_PRECISION, "\
106
- "F.RDB$FIELD_SCALE * -1, F.RDB$FIELD_SUB_TYPE "\
107
- "FROM RDB$RELATION_FIELDS RF, RDB$FIELDS F "\
108
- "WHERE RF.RDB$RELATION_NAME = UPPER('#{table}') "\
109
- "AND RF.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME"
110
-
111
- connection.start_transaction do |tx|
112
- tx.execute(sql) do |row|
113
- sql_type = SQLType.to_base_type(row[1], row[5])
114
- type = nil
115
- case sql_type
116
- when BLOB
117
- type = SQLType.new(sql_type, nil, nil, nil, row[5])
118
-
119
- when CHAR, VARCHAR
120
- type = SQLType.new(sql_type, row[2])
121
-
122
- when DECIMAL, NUMERIC
123
- type = SQLType.new(sql_type, nil, row[3], row[4])
124
-
125
- else
126
- type = SQLType.new(sql_type)
127
- end
128
- types[row[0].strip] = type
129
- end
130
-
131
- end
132
- end
133
- types
134
- end
135
-
136
-
137
- # This method overloads the equivalence test operator for the SQLType
138
- # class.
139
- #
140
- # ==== Parameters
141
- # object:: A reference to the object to be compared with.
142
- def ==(object)
143
- result = false
144
- if object.instance_of?(SQLType)
145
- result = (@type == object.type &&
146
- @length == object.length &&
147
- @precision == object.precision &&
148
- @scale == object.scale &&
149
- @subtype == object.subtype)
150
- end
151
- result
152
- end
153
-
154
-
155
- # This method generates a textual description for a SQLType object.
156
- def to_s
157
- if @type == SQLType::DECIMAL or @type == SQLType::NUMERIC
158
- "#{@type.id2name}(#{@precision},#{@scale})"
159
- elsif @type == SQLType::BLOB
160
- "#{@type.id2name} SUB TYPE #{@subtype}"
161
- elsif @type == SQLType::CHAR or @type == SQLType::VARCHAR
162
- "#{@type.id2name}(#{@length})"
163
- else
164
- @type.id2name
165
- end
166
- end
167
-
168
-
169
- # This class method converts a InterBase internal type to a SQLType base
170
- # type.
171
- #
172
- # ==== Parameters
173
- # type:: A reference to the Interbase field type value.
174
- # subtype:: A reference to the Interbase field subtype value.
175
- def SQLType.to_base_type(type, subtype)
176
- case type
177
- when 16 # DECIMAL, NUMERIC
178
- if subtype
179
- subtype == 1 ? SQLType::NUMERIC : SQLType::DECIMAL
180
- else
181
- SQLType::BIGINT
182
- end
183
- when 17 # BOOLEAN
184
- SQLType::BOOLEAN
185
-
186
- when 261 # BLOB
187
- SQLType::BLOB
188
-
189
- when 14 # CHAR
190
- SQLType::CHAR
191
-
192
- when 12 # DATE
193
- SQLType::DATE
194
-
195
- when 27 # DOUBLE, DECIMAL, NUMERIC
196
- if subtype
197
- subtype == 1 ? SQLType::NUMERIC : SQLType::DECIMAL
198
- else
199
- SQLType::DOUBLE
200
- end
201
-
202
- when 10 # FLOAT
203
- SQLType::FLOAT
204
-
205
- when 8 # INTEGER, DECIMAL, NUMERIC
206
- if subtype
207
- subtype == 1 ? SQLType::NUMERIC : SQLType::DECIMAL
208
- else
209
- SQLType::INTEGER
210
- end
211
-
212
- when 7 # SMALLINT, DECIMAL, NUMERIC
213
- if subtype
214
- subtype == 1 ? SQLType::NUMERIC : SQLType::DECIMAL
215
- else
216
- SQLType::SMALLINT
217
- end
218
-
219
- when 13 # TIME
220
- SQLType::TIME
221
-
222
- when 35 # TIMESTAMP
223
- SQLType::TIMESTAMP
224
-
225
- when 37 # VARCHAR
226
- SQLType::VARCHAR
227
- end
228
- end
229
- end # End of the SQLType class.
230
- end # End of the FireRuby module.