sequel 3.39.0 → 3.40.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +30 -0
- data/README.rdoc +4 -3
- data/doc/active_record.rdoc +1 -1
- data/doc/opening_databases.rdoc +7 -0
- data/doc/release_notes/3.40.0.txt +73 -0
- data/lib/sequel/adapters/ado.rb +29 -3
- data/lib/sequel/adapters/ado/access.rb +334 -0
- data/lib/sequel/adapters/ado/mssql.rb +0 -6
- data/lib/sequel/adapters/cubrid.rb +143 -0
- data/lib/sequel/adapters/jdbc.rb +26 -18
- data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
- data/lib/sequel/adapters/jdbc/derby.rb +7 -7
- data/lib/sequel/adapters/jdbc/hsqldb.rb +5 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -4
- data/lib/sequel/adapters/mysql.rb +0 -3
- data/lib/sequel/adapters/mysql2.rb +0 -3
- data/lib/sequel/adapters/oracle.rb +4 -1
- data/lib/sequel/adapters/postgres.rb +4 -4
- data/lib/sequel/adapters/shared/access.rb +205 -3
- data/lib/sequel/adapters/shared/cubrid.rb +216 -0
- data/lib/sequel/adapters/shared/db2.rb +7 -2
- data/lib/sequel/adapters/shared/mssql.rb +3 -34
- data/lib/sequel/adapters/shared/mysql.rb +4 -33
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +11 -0
- data/lib/sequel/adapters/shared/oracle.rb +5 -0
- data/lib/sequel/adapters/shared/postgres.rb +2 -1
- data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/query.rb +30 -7
- data/lib/sequel/database/schema_methods.rb +7 -2
- data/lib/sequel/dataset/query.rb +9 -10
- data/lib/sequel/dataset/sql.rb +14 -26
- data/lib/sequel/extensions/pg_hstore.rb +19 -0
- data/lib/sequel/extensions/pg_row.rb +5 -5
- data/lib/sequel/plugins/association_pks.rb +121 -18
- data/lib/sequel/plugins/json_serializer.rb +19 -0
- data/lib/sequel/sql.rb +11 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +42 -0
- data/spec/core/database_spec.rb +17 -0
- data/spec/core/dataset_spec.rb +11 -0
- data/spec/core/expression_filters_spec.rb +13 -0
- data/spec/extensions/association_pks_spec.rb +163 -3
- data/spec/extensions/pg_hstore_spec.rb +6 -0
- data/spec/extensions/pg_row_spec.rb +17 -0
- data/spec/integration/associations_test.rb +1 -1
- data/spec/integration/dataset_test.rb +13 -13
- data/spec/integration/plugin_test.rb +232 -7
- data/spec/integration/schema_test.rb +8 -12
- data/spec/integration/spec_helper.rb +1 -1
- data/spec/integration/type_test.rb +6 -0
- metadata +9 -2
data/CHANGELOG
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
=== 3.40.0 (2012-09-26)
|
2
|
+
|
3
|
+
* Add a cubrid adapter for accessing CUBRID databases via the cubrid gem (jeremyevans)
|
4
|
+
|
5
|
+
* Add a jdbc/cubrid adapter for accessing CUBRID databases via JDBC on JRuby (jeremyevans)
|
6
|
+
|
7
|
+
* Return OCI8::CLOB values as ruby Strings in the Oracle adapter (jeremyevans)
|
8
|
+
|
9
|
+
* Use clob for String :text=>true types on Oracle, DB2, HSQLDB, and Derby (jeremyevans) (#555)
|
10
|
+
|
11
|
+
* Allowing marshalling of Sequel::Postgres::HStore (jeremyevans) (#556)
|
12
|
+
|
13
|
+
* Quote channel identifier names when using LISTEN/NOTIFY on PostgreSQL (jeremyevans)
|
14
|
+
|
15
|
+
* Handle nil values when formatting bound variable arguments in the pg_row extension (jeremyevans) (#548)
|
16
|
+
|
17
|
+
* Handle nil values when parsing composite types in the pg_row extension (jeremyevans) (#548)
|
18
|
+
|
19
|
+
* Add :disconnect=>:retry option to Database#transaction, for automatically retrying the transaction on disconnect (jeremyevans)
|
20
|
+
|
21
|
+
* Greatly improved support on Microsoft Access (jeremyevans)
|
22
|
+
|
23
|
+
* Support Database#{schema,tables,views,indexes,foreign_key_list} when using ado/access adapter (ericgj) (#545, #546)
|
24
|
+
|
25
|
+
* Add ado/access adapter for accessing Microsoft Access via the ado adapter (jeremyevans)
|
26
|
+
|
27
|
+
* Combine disconnect error detection for mysql and mysql2 adapters (jeremyevans)
|
28
|
+
|
29
|
+
* Update the association_pks plugin to handle composite primary keys (chanks, jeremyevans) (#544)
|
30
|
+
|
1
31
|
=== 3.39.0 (2012-09-01)
|
2
32
|
|
3
33
|
* Fix defaults_setter to set false default values (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -11,9 +11,10 @@ toolkit for Ruby.
|
|
11
11
|
statements, bound variables, stored procedures, savepoints,
|
12
12
|
two-phase commit, transaction isolation, master/slave
|
13
13
|
configurations, and database sharding.
|
14
|
-
* Sequel currently has adapters for ADO, Amalgalite,
|
15
|
-
DB2, DBI, Firebird, IBM_DB, Informix, JDBC, MySQL,
|
16
|
-
OpenBase, Oracle, PostgreSQL, SQLite3, Swift, and
|
14
|
+
* Sequel currently has adapters for ADO, Amalgalite, CUBRID,
|
15
|
+
DataObjects, DB2, DBI, Firebird, IBM_DB, Informix, JDBC, MySQL,
|
16
|
+
Mysql2, ODBC, OpenBase, Oracle, PostgreSQL, SQLite3, Swift, and
|
17
|
+
TinyTDS.
|
17
18
|
|
18
19
|
== Resources
|
19
20
|
|
data/doc/active_record.rdoc
CHANGED
@@ -456,7 +456,7 @@ This part of the guide will list Sequel equivalents for ActiveRecord methods, ho
|
|
456
456
|
|
457
457
|
==== +abstract_class+, <tt>abstract_class=</tt>, <tt>abstract_class?</tt>
|
458
458
|
|
459
|
-
With Sequel, these methods don't exist because it doesn't default to using single table inheritance in subclasses. ActiveRecord assumes that subclasses of Model classes use single table inheritance, and you have to set <tt>abstract_class = true</tt> to use an abstract class. In Sequel, you must use the +single_table_inheritance+ or +
|
459
|
+
With Sequel, these methods don't exist because it doesn't default to using single table inheritance in subclasses. ActiveRecord assumes that subclasses of Model classes use single table inheritance, and you have to set <tt>abstract_class = true</tt> to use an abstract class. In Sequel, you must use the +single_table_inheritance+ or +class_table_inheritance+ plugin to configure inheritance in the database.
|
460
460
|
|
461
461
|
==== +all+
|
462
462
|
|
data/doc/opening_databases.rdoc
CHANGED
@@ -148,6 +148,12 @@ Without a database argument, assumes a memory database, so you can do:
|
|
148
148
|
Handles paths in the connection string similar to the SQLite adapter, so see
|
149
149
|
the sqlite section below for details.
|
150
150
|
|
151
|
+
=== cubrid
|
152
|
+
|
153
|
+
cubrid is a ruby extension for accessing a CUBRID database. Currently,
|
154
|
+
the ruby cubrid gem is in fairly rough state, with broken transaction
|
155
|
+
support and some other issues, but most things work.
|
156
|
+
|
151
157
|
=== db2
|
152
158
|
|
153
159
|
Requires: db2/db2cli
|
@@ -266,6 +272,7 @@ Example connection strings:
|
|
266
272
|
jdbc:db2://localhost:3700/database:user=user;password=password;
|
267
273
|
jdbc:firebirdsql:localhost/3050:/path/to/database.fdb
|
268
274
|
jdbc:jdbcprogress:T:hostname:port:database
|
275
|
+
jdbc:cubrid:hostname:port:database:::
|
269
276
|
|
270
277
|
You can also use JNDI connection strings:
|
271
278
|
|
@@ -0,0 +1,73 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Sequel now has vastly improved support for Microsoft Access.
|
4
|
+
|
5
|
+
* Sequel now supports the CUBRID database, with a cubrid adapter
|
6
|
+
that uses the cubrid gem, and a jdbc/cubrid adapter for accessing
|
7
|
+
CUBRID via JDBC on JRuby.
|
8
|
+
|
9
|
+
* The association_pks plugin now supports composite keys.
|
10
|
+
|
11
|
+
* Database#transaction now accepts a :disconnect=>:retry option,
|
12
|
+
in which case it will automatically retry the block if it
|
13
|
+
detects a disconnection. This is potentially dangerous, and
|
14
|
+
should only be used if the entire block is idempotent. There
|
15
|
+
is also no checking against an infinite retry loop.
|
16
|
+
|
17
|
+
* SQL::CaseExpression#with_merged_expression has been added, for
|
18
|
+
converting a CaseExpression with an associated expression to
|
19
|
+
one without an associated expression, by merging the expression
|
20
|
+
into each condition.
|
21
|
+
|
22
|
+
= Other Improvements
|
23
|
+
|
24
|
+
* Sequel now quotes arguments/columns in common table expressions.
|
25
|
+
|
26
|
+
* Sequel now handles nil values correctly in the pg_row extension.
|
27
|
+
|
28
|
+
* Sequel::Postgres::HStore instances can now be marshalled.
|
29
|
+
|
30
|
+
* Sequel now uses clob for String :text=>true types on databases that
|
31
|
+
don't support a text type.
|
32
|
+
|
33
|
+
* On PostgreSQL, Sequel now quotes channel identifier names when using
|
34
|
+
LISTEN/NOTIFY.
|
35
|
+
|
36
|
+
* On PostgreSQL, Sequel now correctly handles the case where named
|
37
|
+
type conversion procs have been added before the Database object is
|
38
|
+
instantiated.
|
39
|
+
|
40
|
+
* On DB2, Sequel now explicitly sets NOT NULL for unique constraint
|
41
|
+
columns instead of foreign key columns. DB2 does not allow columns
|
42
|
+
in unique constraints to be NULL, but does allow foreign key columns
|
43
|
+
to be NULL.
|
44
|
+
|
45
|
+
* In the oracle adapter, clob values are now returned as ruby strings
|
46
|
+
upon retrieval.
|
47
|
+
|
48
|
+
* Sequel now detects more types of disconnections in the postgres,
|
49
|
+
mysql, and mysql2 adapters.
|
50
|
+
|
51
|
+
* If a database provides a default column value that isn't a ruby
|
52
|
+
string, it is used directly as the ruby default, instead of causing
|
53
|
+
the schema parsing to fail.
|
54
|
+
|
55
|
+
= Backwards Compatibility
|
56
|
+
|
57
|
+
* Code using Sequel's oracle adapter that expected clob values to be
|
58
|
+
returned as OCI8::CLOB instances needs to be modified to work with
|
59
|
+
ruby strings.
|
60
|
+
|
61
|
+
* Because Sequel now quotes column names in common table expressions,
|
62
|
+
those names are now case sensitive, which could break certain poorly
|
63
|
+
coded queries. Similar issues exist with the quoting of channel
|
64
|
+
identifier names in LISTEN/NOTIFY on PostgreSQL.
|
65
|
+
|
66
|
+
* The private Database#requires_return_generated_keys? method
|
67
|
+
has been removed from the jdbc adapter. Custom jdbc subadapters
|
68
|
+
relying on this method should override the private
|
69
|
+
Database#execute_statement_insert method instead to ensure that
|
70
|
+
RETURN_GENERATED_KEYS is used for insert statements.
|
71
|
+
|
72
|
+
* The private Dataset#argument_list and #argument_list_append methods
|
73
|
+
have been removed.
|
data/lib/sequel/adapters/ado.rb
CHANGED
@@ -12,9 +12,9 @@ module Sequel
|
|
12
12
|
super
|
13
13
|
case @opts[:conn_string]
|
14
14
|
when /Microsoft\.(Jet|ACE)\.OLEDB/io
|
15
|
-
Sequel.ts_require 'adapters/
|
16
|
-
extend Sequel::Access::DatabaseMethods
|
17
|
-
|
15
|
+
Sequel.ts_require 'adapters/ado/access'
|
16
|
+
extend Sequel::ADO::Access::DatabaseMethods
|
17
|
+
@dataset_class = ADO::Access::Dataset
|
18
18
|
else
|
19
19
|
@opts[:driver] ||= 'SQL Server'
|
20
20
|
case @opts[:driver]
|
@@ -57,6 +57,32 @@ module Sequel
|
|
57
57
|
handle
|
58
58
|
end
|
59
59
|
|
60
|
+
# Just execute so it doesn't attempt to return the number of rows modified.
|
61
|
+
def execute_ddl(sql, opts={})
|
62
|
+
execute(sql, opts)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Just execute so it doesn't attempt to return the number of rows modified.
|
66
|
+
def execute_insert(sql, opts={})
|
67
|
+
execute(sql, opts)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Use pass by reference in WIN32OLE to get the number of affected rows,
|
71
|
+
# unless is a provider is in use (since some providers don't seem to
|
72
|
+
# return the number of affected rows, but the default provider appears
|
73
|
+
# to).
|
74
|
+
def execute_dui(sql, opts={})
|
75
|
+
return super if opts[:provider]
|
76
|
+
synchronize(opts[:server]) do |conn|
|
77
|
+
begin
|
78
|
+
log_yield(sql){conn.Execute(sql, 1)}
|
79
|
+
WIN32OLE::ARGV[1]
|
80
|
+
rescue ::WIN32OLERuntimeError => e
|
81
|
+
raise_error(e)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
60
86
|
def execute(sql, opts={})
|
61
87
|
synchronize(opts[:server]) do |conn|
|
62
88
|
begin
|
@@ -0,0 +1,334 @@
|
|
1
|
+
Sequel.require 'adapters/shared/access'
|
2
|
+
Sequel.require 'adapters/utils/split_alter_table'
|
3
|
+
|
4
|
+
module Sequel
|
5
|
+
module ADO
|
6
|
+
# Database and Dataset instance methods for Access specific
|
7
|
+
# support via ADO.
|
8
|
+
module Access
|
9
|
+
class AdoSchema
|
10
|
+
QUERY_TYPE = {
|
11
|
+
:columns => 4,
|
12
|
+
:indexes => 12,
|
13
|
+
:tables => 20,
|
14
|
+
:views => 23,
|
15
|
+
:foreign_keys => 27
|
16
|
+
}
|
17
|
+
|
18
|
+
attr_reader :type, :criteria
|
19
|
+
|
20
|
+
def initialize(type, crit)
|
21
|
+
@type = QUERY_TYPE[type]
|
22
|
+
@criteria = Array(crit)
|
23
|
+
end
|
24
|
+
|
25
|
+
class Column
|
26
|
+
DATA_TYPE = {
|
27
|
+
2 => "SMALLINT",
|
28
|
+
3 => "INTEGER",
|
29
|
+
4 => "REAL",
|
30
|
+
5 => "DOUBLE",
|
31
|
+
6 => "MONEY",
|
32
|
+
7 => "DATETIME",
|
33
|
+
11 => "BIT",
|
34
|
+
14 => "DECIMAL",
|
35
|
+
16 => "TINYINT",
|
36
|
+
17 => "BYTE",
|
37
|
+
72 => "GUID",
|
38
|
+
128 => "BINARY",
|
39
|
+
130 => "TEXT",
|
40
|
+
131 => "DECIMAL",
|
41
|
+
201 => "TEXT",
|
42
|
+
205 => "IMAGE"
|
43
|
+
}
|
44
|
+
|
45
|
+
def initialize(row)
|
46
|
+
@row = row
|
47
|
+
end
|
48
|
+
|
49
|
+
def [](col)
|
50
|
+
@row[col]
|
51
|
+
end
|
52
|
+
|
53
|
+
def allow_null
|
54
|
+
self["IS_NULLABLE"]
|
55
|
+
end
|
56
|
+
|
57
|
+
def default
|
58
|
+
self["COLUMN_DEFAULT"]
|
59
|
+
end
|
60
|
+
|
61
|
+
def db_type
|
62
|
+
t = DATA_TYPE[self["DATA_TYPE"]]
|
63
|
+
if t == "DECIMAL" && precision
|
64
|
+
t + "(#{precision.to_i},#{(scale || 0).to_i})"
|
65
|
+
elsif t == "TEXT" && maximum_length && maximum_length > 0
|
66
|
+
t + "(#{maximum_length.to_i})"
|
67
|
+
else
|
68
|
+
t
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def precision
|
73
|
+
self["NUMERIC_PRECISION"]
|
74
|
+
end
|
75
|
+
|
76
|
+
def scale
|
77
|
+
self["NUMERIC_SCALE"]
|
78
|
+
end
|
79
|
+
|
80
|
+
def maximum_length
|
81
|
+
self["CHARACTER_MAXIMUM_LENGTH"]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
module DatabaseMethods
|
87
|
+
include Sequel::Access::DatabaseMethods
|
88
|
+
include Sequel::Database::SplitAlterTable
|
89
|
+
|
90
|
+
DECIMAL_TYPE_RE = /decimal/io
|
91
|
+
LAST_INSERT_ID = "SELECT @@IDENTITY".freeze
|
92
|
+
|
93
|
+
# Remove cached schema after altering a table, since otherwise it can be cached
|
94
|
+
# incorrectly in the rename column case.
|
95
|
+
def alter_table(name, *)
|
96
|
+
super
|
97
|
+
remove_cached_schema(name)
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
|
101
|
+
# Access doesn't let you disconnect if inside a transaction, so
|
102
|
+
# try rolling back an existing transaction first.
|
103
|
+
def disconnect_connection(conn)
|
104
|
+
conn.RollbackTrans rescue nil
|
105
|
+
super
|
106
|
+
end
|
107
|
+
|
108
|
+
def execute_insert(sql, opts={})
|
109
|
+
synchronize(opts[:server]) do |conn|
|
110
|
+
begin
|
111
|
+
r = log_yield(sql){conn.Execute(sql)}
|
112
|
+
res = log_yield(LAST_INSERT_ID){conn.Execute(LAST_INSERT_ID)}
|
113
|
+
res.getRows.transpose.each{|r| return r.shift}
|
114
|
+
rescue ::WIN32OLERuntimeError => e
|
115
|
+
raise_error(e)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def tables(opts={})
|
122
|
+
m = output_identifier_meth
|
123
|
+
ado_schema_tables.map {|tbl| m.call(tbl['TABLE_NAME'])}
|
124
|
+
end
|
125
|
+
|
126
|
+
def views(opts={})
|
127
|
+
m = output_identifier_meth
|
128
|
+
ado_schema_views.map {|tbl| m.call(tbl['TABLE_NAME'])}
|
129
|
+
end
|
130
|
+
|
131
|
+
# Note OpenSchema returns compound indexes as multiple rows
|
132
|
+
def indexes(table_name,opts={})
|
133
|
+
m = output_identifier_meth
|
134
|
+
idxs = ado_schema_indexes(table_name).inject({}) do |memo, idx|
|
135
|
+
unless idx["PRIMARY_KEY"]
|
136
|
+
index = memo[m.call(idx["INDEX_NAME"])] ||= {
|
137
|
+
:columns=>[], :unique=>idx["UNIQUE"]
|
138
|
+
}
|
139
|
+
index[:columns] << m.call(idx["COLUMN_NAME"])
|
140
|
+
end
|
141
|
+
memo
|
142
|
+
end
|
143
|
+
idxs
|
144
|
+
end
|
145
|
+
|
146
|
+
# Note OpenSchema returns compound foreign key relationships as multiple rows
|
147
|
+
def foreign_key_list(table, opts={})
|
148
|
+
m = output_identifier_meth
|
149
|
+
fks = ado_schema_foreign_keys(table).inject({}) do |memo, fk|
|
150
|
+
name = m.call(fk['FK_NAME'])
|
151
|
+
specs = memo[name] ||= {
|
152
|
+
:columns => [],
|
153
|
+
:table => m.call(fk['PK_TABLE_NAME']),
|
154
|
+
:key => [],
|
155
|
+
:deferrable => fk['DEFERRABILITY'],
|
156
|
+
:name => name,
|
157
|
+
:on_delete => fk['DELETE_RULE'],
|
158
|
+
:on_update => fk['UPDATE_RULE']
|
159
|
+
}
|
160
|
+
specs[:columns] << m.call(fk['FK_COLUMN_NAME'])
|
161
|
+
specs[:key] << m.call(fk['PK_COLUMN_NAME'])
|
162
|
+
memo
|
163
|
+
end
|
164
|
+
fks.values
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
# Emulate rename_column by adding the column, copying data from the old
|
170
|
+
# column, and dropping the old column.
|
171
|
+
def alter_table_sql(table, op)
|
172
|
+
case op[:op]
|
173
|
+
when :rename_column
|
174
|
+
unless sch = op[:schema]
|
175
|
+
raise(Error, "can't find existing schema entry for #{op[:name]}") unless sch = op[:schema] || schema(table).find{|c| c.first == op[:name]}
|
176
|
+
sch = sch.last
|
177
|
+
end
|
178
|
+
[
|
179
|
+
alter_table_sql(table, :op=>:add_column, :name=>op[:new_name], :default=>sch[:ruby_default], :type=>sch[:db_type], :null=>sch[:allow_null]),
|
180
|
+
from(table).update_sql(op[:new_name]=>op[:name]),
|
181
|
+
alter_table_sql(table, :op=>:drop_column, :name=>op[:name])
|
182
|
+
]
|
183
|
+
when :set_column_null, :set_column_default
|
184
|
+
raise(Error, "can't find existing schema entry for #{op[:name]}") unless sch = op[:schema] || schema(table).find{|c| c.first == op[:name]}
|
185
|
+
sch = sch.last
|
186
|
+
|
187
|
+
sch = if op[:op] == :set_column_null
|
188
|
+
sch.merge(:allow_null=>op[:null])
|
189
|
+
else
|
190
|
+
sch.merge(:ruby_default=>op[:default])
|
191
|
+
end
|
192
|
+
|
193
|
+
[
|
194
|
+
alter_table_sql(table, :op=>:rename_column, :name=>op[:name], :new_name=>:sequel_access_backup_column, :schema=>sch),
|
195
|
+
alter_table_sql(table, :op=>:rename_column, :new_name=>op[:name], :name=>:sequel_access_backup_column, :schema=>sch)
|
196
|
+
]
|
197
|
+
else
|
198
|
+
super
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def begin_transaction(conn, opts={})
|
203
|
+
log_yield('Transaction.begin'){conn.BeginTrans}
|
204
|
+
end
|
205
|
+
|
206
|
+
def commit_transaction(conn, opts={})
|
207
|
+
log_yield('Transaction.commit'){conn.CommitTrans}
|
208
|
+
end
|
209
|
+
|
210
|
+
def rollback_transaction(conn, opts={})
|
211
|
+
log_yield('Transaction.rollback'){conn.RollbackTrans}
|
212
|
+
end
|
213
|
+
|
214
|
+
def schema_column_type(db_type)
|
215
|
+
case db_type.downcase
|
216
|
+
when 'bit'
|
217
|
+
:boolean
|
218
|
+
when 'byte', 'guid'
|
219
|
+
:integer
|
220
|
+
when 'image'
|
221
|
+
:blob
|
222
|
+
else
|
223
|
+
super
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def schema_parse_table(table_name, opts)
|
228
|
+
m = output_identifier_meth(opts[:dataset])
|
229
|
+
m2 = input_identifier_meth(opts[:dataset])
|
230
|
+
tn = m2.call(table_name.to_s)
|
231
|
+
idxs = ado_schema_indexes(tn)
|
232
|
+
ado_schema_columns(tn).map {|row|
|
233
|
+
specs = {
|
234
|
+
:allow_null => row.allow_null,
|
235
|
+
:db_type => row.db_type,
|
236
|
+
:default => row.default,
|
237
|
+
:primary_key => !!idxs.find {|idx|
|
238
|
+
idx["COLUMN_NAME"] == row["COLUMN_NAME"] &&
|
239
|
+
idx["PRIMARY_KEY"]
|
240
|
+
},
|
241
|
+
:type => if row.db_type =~ DECIMAL_TYPE_RE && row.scale == 0
|
242
|
+
:integer
|
243
|
+
else
|
244
|
+
schema_column_type(row.db_type)
|
245
|
+
end,
|
246
|
+
:ado_type => row["DATA_TYPE"]
|
247
|
+
}
|
248
|
+
specs[:default] = nil if blank_object?(specs[:default])
|
249
|
+
specs[:allow_null] = specs[:allow_null] && !specs[:primary_key]
|
250
|
+
[ m.call(row["COLUMN_NAME"]), specs ]
|
251
|
+
}
|
252
|
+
end
|
253
|
+
|
254
|
+
def ado_schema_tables
|
255
|
+
rows=[]
|
256
|
+
fetch_ado_schema(:tables, [nil,nil,nil,'TABLE']) do |row|
|
257
|
+
rows << row
|
258
|
+
end
|
259
|
+
rows
|
260
|
+
end
|
261
|
+
|
262
|
+
def ado_schema_views
|
263
|
+
rows=[]
|
264
|
+
fetch_ado_schema(:views, [nil,nil,nil]) do |row|
|
265
|
+
rows << row
|
266
|
+
end
|
267
|
+
rows
|
268
|
+
end
|
269
|
+
|
270
|
+
def ado_schema_indexes(table_name)
|
271
|
+
rows=[]
|
272
|
+
fetch_ado_schema(:indexes, [nil,nil,nil,nil,table_name.to_s]) do |row|
|
273
|
+
rows << row
|
274
|
+
end
|
275
|
+
rows
|
276
|
+
end
|
277
|
+
|
278
|
+
def ado_schema_columns(table_name)
|
279
|
+
rows=[]
|
280
|
+
fetch_ado_schema(:columns, [nil,nil,table_name.to_s,nil]) do |row|
|
281
|
+
rows << AdoSchema::Column.new(row)
|
282
|
+
end
|
283
|
+
rows.sort!{|a,b| a["ORDINAL_POSITION"] <=> b["ORDINAL_POSITION"]}
|
284
|
+
end
|
285
|
+
|
286
|
+
def ado_schema_foreign_keys(table_name)
|
287
|
+
rows=[]
|
288
|
+
fetch_ado_schema(:foreign_keys, [nil,nil,nil,nil,nil,table_name.to_s]) do |row|
|
289
|
+
rows << row
|
290
|
+
end
|
291
|
+
rows.sort!{|a,b| a["ORDINAL"] <=> b["ORDINAL"]}
|
292
|
+
end
|
293
|
+
|
294
|
+
def fetch_ado_schema(type, criteria=[])
|
295
|
+
execute_open_ado_schema(type, criteria) do |s|
|
296
|
+
cols = s.Fields.extend(Enumerable).map {|c| c.Name}
|
297
|
+
s.getRows.transpose.each do |r|
|
298
|
+
row = {}
|
299
|
+
cols.each{|c| row[c] = r.shift}
|
300
|
+
yield row
|
301
|
+
end unless s.eof
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# This is like execute() in that it yields an ADO RecordSet, except
|
306
|
+
# instead of an SQL interface there's this OpenSchema call
|
307
|
+
# cf. http://msdn.microsoft.com/en-us/library/ee275721(v=bts.10)
|
308
|
+
#
|
309
|
+
def execute_open_ado_schema(type, criteria=[])
|
310
|
+
ado_schema = AdoSchema.new(type, criteria)
|
311
|
+
synchronize(opts[:server]) do |conn|
|
312
|
+
begin
|
313
|
+
r = log_yield("OpenSchema #{type.inspect}, #{criteria.inspect}") {
|
314
|
+
if ado_schema.criteria.empty?
|
315
|
+
conn.OpenSchema(ado_schema.type)
|
316
|
+
else
|
317
|
+
conn.OpenSchema(ado_schema.type, ado_schema.criteria)
|
318
|
+
end
|
319
|
+
}
|
320
|
+
yield(r) if block_given?
|
321
|
+
rescue ::WIN32OLERuntimeError => e
|
322
|
+
raise_error(e)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
nil
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
class Dataset < ADO::Dataset
|
330
|
+
include Sequel::Access::DatasetMethods
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|