neo-activerecord-jdbc-adapter 5.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.travis.yml +438 -0
  4. data/.yardopts +4 -0
  5. data/Appraisals +41 -0
  6. data/CONTRIBUTING.md +44 -0
  7. data/Gemfile +62 -0
  8. data/History.md +1191 -0
  9. data/LICENSE.txt +25 -0
  10. data/README.md +266 -0
  11. data/RUNNING_TESTS.md +88 -0
  12. data/Rakefile +100 -0
  13. data/Rakefile.jdbc +20 -0
  14. data/activerecord-jdbc-adapter.gemspec +42 -0
  15. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  16. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
  18. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  19. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  20. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
  21. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  22. data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
  23. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  24. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  25. data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
  26. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
  28. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  31. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  32. data/lib/activerecord-jdbc-adapter.rb +1 -0
  33. data/lib/arel/visitors/compat.rb +60 -0
  34. data/lib/arel/visitors/db2.rb +137 -0
  35. data/lib/arel/visitors/derby.rb +112 -0
  36. data/lib/arel/visitors/firebird.rb +79 -0
  37. data/lib/arel/visitors/h2.rb +25 -0
  38. data/lib/arel/visitors/hsqldb.rb +32 -0
  39. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  40. data/lib/arel/visitors/sql_server.rb +225 -0
  41. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  42. data/lib/arjdbc.rb +19 -0
  43. data/lib/arjdbc/abstract/database_statements.rb +92 -0
  44. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  45. data/lib/arjdbc/common_jdbc_methods.rb +13 -0
  46. data/lib/arjdbc/db2.rb +4 -0
  47. data/lib/arjdbc/db2/adapter.rb +789 -0
  48. data/lib/arjdbc/db2/as400.rb +130 -0
  49. data/lib/arjdbc/db2/column.rb +167 -0
  50. data/lib/arjdbc/db2/connection_methods.rb +44 -0
  51. data/lib/arjdbc/derby.rb +3 -0
  52. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  53. data/lib/arjdbc/derby/adapter.rb +556 -0
  54. data/lib/arjdbc/derby/connection_methods.rb +20 -0
  55. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  56. data/lib/arjdbc/discover.rb +115 -0
  57. data/lib/arjdbc/firebird.rb +4 -0
  58. data/lib/arjdbc/firebird/adapter.rb +434 -0
  59. data/lib/arjdbc/firebird/connection_methods.rb +23 -0
  60. data/lib/arjdbc/h2.rb +3 -0
  61. data/lib/arjdbc/h2/adapter.rb +303 -0
  62. data/lib/arjdbc/h2/connection_methods.rb +27 -0
  63. data/lib/arjdbc/hsqldb.rb +3 -0
  64. data/lib/arjdbc/hsqldb/adapter.rb +297 -0
  65. data/lib/arjdbc/hsqldb/connection_methods.rb +28 -0
  66. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  67. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  68. data/lib/arjdbc/informix.rb +5 -0
  69. data/lib/arjdbc/informix/adapter.rb +162 -0
  70. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  71. data/lib/arjdbc/jdbc.rb +59 -0
  72. data/lib/arjdbc/jdbc/adapter.rb +899 -0
  73. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  74. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  75. data/lib/arjdbc/jdbc/base_ext.rb +44 -0
  76. data/lib/arjdbc/jdbc/callbacks.rb +51 -0
  77. data/lib/arjdbc/jdbc/column.rb +97 -0
  78. data/lib/arjdbc/jdbc/connection.rb +133 -0
  79. data/lib/arjdbc/jdbc/connection_methods.rb +36 -0
  80. data/lib/arjdbc/jdbc/driver.rb +43 -0
  81. data/lib/arjdbc/jdbc/extension.rb +59 -0
  82. data/lib/arjdbc/jdbc/java.rb +15 -0
  83. data/lib/arjdbc/jdbc/jdbc.rake +4 -0
  84. data/lib/arjdbc/jdbc/quoted_primary_key.rb +28 -0
  85. data/lib/arjdbc/jdbc/railtie.rb +2 -0
  86. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -0
  87. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
  88. data/lib/arjdbc/jdbc/type_cast.rb +166 -0
  89. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  90. data/lib/arjdbc/mimer.rb +3 -0
  91. data/lib/arjdbc/mimer/adapter.rb +142 -0
  92. data/lib/arjdbc/mssql.rb +7 -0
  93. data/lib/arjdbc/mssql/adapter.rb +808 -0
  94. data/lib/arjdbc/mssql/column.rb +200 -0
  95. data/lib/arjdbc/mssql/connection_methods.rb +79 -0
  96. data/lib/arjdbc/mssql/explain_support.rb +99 -0
  97. data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
  98. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  99. data/lib/arjdbc/mssql/types.rb +343 -0
  100. data/lib/arjdbc/mssql/utils.rb +82 -0
  101. data/lib/arjdbc/mysql.rb +3 -0
  102. data/lib/arjdbc/mysql/adapter.rb +1006 -0
  103. data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
  104. data/lib/arjdbc/mysql/column.rb +162 -0
  105. data/lib/arjdbc/mysql/connection_methods.rb +145 -0
  106. data/lib/arjdbc/mysql/explain_support.rb +82 -0
  107. data/lib/arjdbc/mysql/schema_creation.rb +58 -0
  108. data/lib/arjdbc/oracle.rb +4 -0
  109. data/lib/arjdbc/oracle/adapter.rb +952 -0
  110. data/lib/arjdbc/oracle/column.rb +126 -0
  111. data/lib/arjdbc/oracle/connection_methods.rb +21 -0
  112. data/lib/arjdbc/postgresql.rb +3 -0
  113. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
  114. data/lib/arjdbc/postgresql/adapter.rb +825 -0
  115. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  116. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  117. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  118. data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
  119. data/lib/arjdbc/postgresql/column.rb +51 -0
  120. data/lib/arjdbc/postgresql/connection_methods.rb +54 -0
  121. data/lib/arjdbc/postgresql/name.rb +24 -0
  122. data/lib/arjdbc/postgresql/oid_types.rb +178 -0
  123. data/lib/arjdbc/railtie.rb +11 -0
  124. data/lib/arjdbc/sqlite3.rb +3 -0
  125. data/lib/arjdbc/sqlite3/adapter.rb +703 -0
  126. data/lib/arjdbc/sqlite3/connection_methods.rb +40 -0
  127. data/lib/arjdbc/sybase.rb +2 -0
  128. data/lib/arjdbc/sybase/adapter.rb +47 -0
  129. data/lib/arjdbc/tasks.rb +13 -0
  130. data/lib/arjdbc/tasks/database_tasks.rb +54 -0
  131. data/lib/arjdbc/tasks/databases.rake +91 -0
  132. data/lib/arjdbc/tasks/databases3.rake +215 -0
  133. data/lib/arjdbc/tasks/databases4.rake +39 -0
  134. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  135. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  136. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  137. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  138. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  139. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  140. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
  141. data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -0
  142. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  143. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  144. data/lib/arjdbc/util/table_copier.rb +110 -0
  145. data/lib/arjdbc/version.rb +8 -0
  146. data/lib/generators/jdbc/USAGE +9 -0
  147. data/lib/generators/jdbc/jdbc_generator.rb +17 -0
  148. data/lib/jdbc_adapter.rb +2 -0
  149. data/lib/jdbc_adapter/rake_tasks.rb +4 -0
  150. data/lib/jdbc_adapter/version.rb +4 -0
  151. data/pom.xml +114 -0
  152. data/rails_generators/jdbc_generator.rb +15 -0
  153. data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
  154. data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
  155. data/rakelib/01-tomcat.rake +51 -0
  156. data/rakelib/02-test.rake +121 -0
  157. data/rakelib/bundler_ext.rb +11 -0
  158. data/rakelib/compile.rake +62 -0
  159. data/rakelib/db.rake +58 -0
  160. data/rakelib/rails.rake +75 -0
  161. data/src/java/arjdbc/ArJdbcModule.java +178 -0
  162. data/src/java/arjdbc/db2/DB2Module.java +71 -0
  163. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +142 -0
  164. data/src/java/arjdbc/derby/DerbyModule.java +179 -0
  165. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +164 -0
  166. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
  167. data/src/java/arjdbc/h2/H2Module.java +44 -0
  168. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +67 -0
  169. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +68 -0
  170. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +75 -0
  171. data/src/java/arjdbc/jdbc/AdapterJavaService.java +45 -0
  172. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  173. data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +45 -0
  174. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3616 -0
  175. data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
  176. data/src/java/arjdbc/mssql/MSSQLModule.java +102 -0
  177. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +195 -0
  178. data/src/java/arjdbc/mysql/MySQLModule.java +147 -0
  179. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +397 -0
  180. data/src/java/arjdbc/oracle/OracleModule.java +75 -0
  181. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +465 -0
  182. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +752 -0
  183. data/src/java/arjdbc/sqlite3/SQLite3Module.java +78 -0
  184. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +351 -0
  185. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  186. data/src/java/arjdbc/util/QuotingUtils.java +111 -0
  187. metadata +255 -0
@@ -0,0 +1,752 @@
1
+ /***** BEGIN LICENSE BLOCK *****
2
+ * Copyright (c) 2012-2013 Karol Bucek <self@kares.org>
3
+ * Copyright (c) 2006-2010 Nick Sieger <nick@nicksieger.com>
4
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
5
+ * Copyright (c) 2008-2009 Thomas E Enebo <enebo@acm.org>
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining
8
+ * a copy of this software and associated documentation files (the
9
+ * "Software"), to deal in the Software without restriction, including
10
+ * without limitation the rights to use, copy, modify, merge, publish,
11
+ * distribute, sublicense, and/or sell copies of the Software, and to
12
+ * permit persons to whom the Software is furnished to do so, subject to
13
+ * the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be
16
+ * included in all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ ***** END LICENSE BLOCK *****/
26
+ package arjdbc.postgresql;
27
+
28
+ import java.io.ByteArrayInputStream;
29
+ import java.io.InputStream;
30
+ import java.sql.Array;
31
+ import java.sql.Connection;
32
+ import java.sql.PreparedStatement;
33
+ import java.sql.ResultSet;
34
+ import java.sql.SQLException;
35
+ import java.sql.Statement;
36
+ import java.sql.Timestamp;
37
+ import java.sql.Types;
38
+ import java.util.Map;
39
+ import java.util.UUID;
40
+
41
+ import org.jruby.Ruby;
42
+ import org.jruby.RubyArray;
43
+ import org.jruby.RubyBoolean;
44
+ import org.jruby.RubyClass;
45
+ import org.jruby.RubyFixnum;
46
+ import org.jruby.RubyFloat;
47
+ import org.jruby.RubyHash;
48
+ import org.jruby.RubyIO;
49
+ import org.jruby.RubyString;
50
+ import org.jruby.anno.JRubyMethod;
51
+ import org.jruby.javasupport.JavaUtil;
52
+ import org.jruby.runtime.ObjectAllocator;
53
+ import org.jruby.runtime.ThreadContext;
54
+ import org.jruby.runtime.builtin.IRubyObject;
55
+ import org.jruby.util.ByteList;
56
+
57
+ import org.postgresql.PGConnection;
58
+ import org.postgresql.PGStatement;
59
+ import org.postgresql.core.BaseConnection;
60
+ import org.postgresql.jdbc4.Jdbc4Array;
61
+ import org.postgresql.util.PGInterval;
62
+ import org.postgresql.util.PGobject;
63
+
64
+ /**
65
+ *
66
+ * @author enebo
67
+ */
68
+ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection {
69
+ private static final long serialVersionUID = 7235537759545717760L;
70
+
71
+ protected PostgreSQLRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
72
+ super(runtime, metaClass);
73
+ }
74
+
75
+ public static RubyClass createPostgreSQLJdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
76
+ final RubyClass clazz = getConnectionAdapters(runtime).
77
+ defineClassUnder("PostgreSQLJdbcConnection", jdbcConnection, POSTGRESQL_JDBCCONNECTION_ALLOCATOR);
78
+ clazz.defineAnnotatedMethods(PostgreSQLRubyJdbcConnection.class);
79
+ getConnectionAdapters(runtime).setConstant("PostgresJdbcConnection", clazz); // backwards-compat
80
+ return clazz;
81
+ }
82
+
83
+ private static ObjectAllocator POSTGRESQL_JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
84
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
85
+ return new PostgreSQLRubyJdbcConnection(runtime, klass);
86
+ }
87
+ };
88
+
89
+ // enables testing if the bug is fixed (please run our test-suite)
90
+ // using `rake test_postgresql JRUBY_OPTS="-J-Darjdbc.postgresql.generated_keys=true"`
91
+ protected static final boolean generatedKeys;
92
+ static {
93
+ String genKeys = System.getProperty("arjdbc.postgresql.generated_keys");
94
+ if ( genKeys == null ) { // @deprecated system property name :
95
+ genKeys = System.getProperty("arjdbc.postgresql.generated.keys");
96
+ }
97
+ generatedKeys = Boolean.parseBoolean(genKeys);
98
+ }
99
+
100
+ @Override
101
+ protected IRubyObject mapGeneratedKeys(
102
+ final Ruby runtime, final Connection connection,
103
+ final Statement statement, final Boolean singleResult)
104
+ throws SQLException {
105
+ // NOTE: PostgreSQL driver supports generated keys but does not work
106
+ // correctly for all cases e.g. for tables whene no keys are generated
107
+ // during an INSERT getGeneratedKeys return all inserted rows instead
108
+ // of an empty result set ... thus disabled until issue is resolved !
109
+ if ( ! generatedKeys ) return null; // not supported
110
+ // NOTE: generated-keys is implemented by the Postgre's JDBC driver by
111
+ // adding a "RETURNING" suffix after the executeUpdate passed query ...
112
+ return super.mapGeneratedKeys(runtime, connection, statement, singleResult);
113
+ }
114
+
115
+ // storesMixedCaseIdentifiers() return false;
116
+ // storesLowerCaseIdentifiers() return true;
117
+ // storesUpperCaseIdentifiers() return false;
118
+
119
+ @Override
120
+ protected String caseConvertIdentifierForRails(final Connection connection, final String value)
121
+ throws SQLException {
122
+ return value;
123
+ }
124
+
125
+ @Override
126
+ protected String caseConvertIdentifierForJdbc(final Connection connection, final String value)
127
+ throws SQLException {
128
+ return value;
129
+ }
130
+
131
+ @Override
132
+ protected Connection newConnection() throws SQLException {
133
+ final Connection connection = getConnectionFactory().newConnection();
134
+ final PGConnection pgConnection;
135
+ if ( connection instanceof PGConnection ) {
136
+ pgConnection = (PGConnection) connection;
137
+ }
138
+ else {
139
+ pgConnection = connection.unwrap(PGConnection.class);
140
+ }
141
+ pgConnection.addDataType("daterange", DateRangeType.class);
142
+ pgConnection.addDataType("tsrange", TsRangeType.class);
143
+ pgConnection.addDataType("tstzrange", TstzRangeType.class);
144
+ pgConnection.addDataType("int4range", Int4RangeType.class);
145
+ pgConnection.addDataType("int8range", Int8RangeType.class);
146
+ pgConnection.addDataType("numrange", NumRangeType.class);
147
+ return connection;
148
+ }
149
+
150
+ @Override // due statement.setNull(index, Types.BLOB) not working :
151
+ // org.postgresql.util.PSQLException: ERROR: column "sample_binary" is of type bytea but expression is of type oid
152
+ protected void setBlobParameter(final ThreadContext context,
153
+ final Connection connection, final PreparedStatement statement,
154
+ final int index, final Object value,
155
+ final IRubyObject column, final int type) throws SQLException {
156
+ if ( value instanceof IRubyObject ) {
157
+ setBlobParameter(context, connection, statement, index, (IRubyObject) value, column, type);
158
+ }
159
+ else {
160
+ if ( value == null ) statement.setNull(index, Types.BINARY);
161
+ else {
162
+ statement.setBinaryStream(index, (InputStream) value);
163
+ }
164
+ }
165
+ }
166
+
167
+ @Override // due statement.setNull(index, Types.BLOB) not working :
168
+ // org.postgresql.util.PSQLException: ERROR: column "sample_binary" is of type bytea but expression is of type oid
169
+ protected void setBlobParameter(final ThreadContext context,
170
+ final Connection connection, final PreparedStatement statement,
171
+ final int index, final IRubyObject value,
172
+ final IRubyObject column, final int type) throws SQLException {
173
+ if ( value.isNil() ) {
174
+ statement.setNull(index, Types.BINARY);
175
+ }
176
+ else {
177
+ if ( value instanceof RubyIO ) { // IO/File
178
+ statement.setBinaryStream(index, ((RubyIO) value).getInStream());
179
+ }
180
+ else { // should be a RubyString
181
+ final ByteList blob = value.asString().getByteList();
182
+ statement.setBinaryStream(index,
183
+ new ByteArrayInputStream(blob.unsafeBytes(), blob.getBegin(), blob.getRealSize()),
184
+ blob.getRealSize() // length
185
+ );
186
+ }
187
+ }
188
+ }
189
+
190
+ @Override // to handle infinity timestamp values
191
+ protected void setTimestampParameter(final ThreadContext context,
192
+ final Connection connection, final PreparedStatement statement,
193
+ final int index, IRubyObject value,
194
+ final IRubyObject column, final int type) throws SQLException {
195
+
196
+ if ( value instanceof RubyFloat ) {
197
+ final double _value = ( (RubyFloat) value ).getValue();
198
+ if ( Double.isInfinite(_value) ) {
199
+ final Timestamp timestamp;
200
+ if ( _value < 0 ) {
201
+ timestamp = new Timestamp(PGStatement.DATE_NEGATIVE_INFINITY);
202
+ }
203
+ else {
204
+ timestamp = new Timestamp(PGStatement.DATE_POSITIVE_INFINITY);
205
+ }
206
+ statement.setTimestamp( index, timestamp );
207
+ return;
208
+ }
209
+ }
210
+
211
+ super.setTimestampParameter(context, connection, statement, index, value, column, type);
212
+ }
213
+
214
+ private static final ByteList INTERVAL =
215
+ new ByteList( new byte[] { 'i','n','t','e','r','v','a','l' }, false );
216
+
217
+ private static final ByteList ARRAY_END = new ByteList( new byte[] { '[',']' }, false );
218
+
219
+ @Override
220
+ protected void setStringParameter(final ThreadContext context,
221
+ final Connection connection, final PreparedStatement statement,
222
+ final int index, final IRubyObject value,
223
+ final IRubyObject column, final int type) throws SQLException {
224
+ final RubyString sqlType;
225
+ if ( column != null && ! column.isNil() ) {
226
+ sqlType = (RubyString) column.callMethod(context, "sql_type");
227
+ }
228
+ else {
229
+ sqlType = null;
230
+ }
231
+
232
+ if ( value.isNil() ) {
233
+ if ( rawArrayType == Boolean.TRUE ) { // array's type is :string
234
+ if ( sqlType != null && sqlType.getByteList().endsWith( ARRAY_END ) ) {
235
+ statement.setNull(index, Types.ARRAY); return;
236
+ }
237
+ statement.setNull(index, type); return;
238
+ }
239
+ statement.setNull(index, Types.VARCHAR);
240
+ }
241
+ else {
242
+ final String valueStr = value.asString().toString();
243
+ if ( sqlType != null ) {
244
+ if ( rawArrayType == Boolean.TRUE && sqlType.getByteList().endsWith( ARRAY_END ) ) {
245
+ final int oid = oid(context, column);
246
+ final Array valueArr = new Jdbc4Array(connection.unwrap(BaseConnection.class), oid, valueStr);
247
+ statement.setArray(index, valueArr); return;
248
+ }
249
+ if ( sqlType.getByteList().startsWith( INTERVAL ) ) {
250
+ statement.setObject( index, new PGInterval( valueStr ) ); return;
251
+ }
252
+ }
253
+ statement.setString( index, valueStr );
254
+ }
255
+ }
256
+
257
+ private static int oid(final ThreadContext context, final IRubyObject column) {
258
+ // our column convention :
259
+ IRubyObject oid = column.getInstanceVariables().getInstanceVariable("@oid");
260
+ if ( oid == null || oid.isNil() ) { // only for user instantiated Column
261
+ throw new IllegalStateException("missing @oid for column: " + column.inspect());
262
+ }
263
+ return RubyFixnum.fix2int(oid);
264
+ }
265
+
266
+ @Override
267
+ protected void setObjectParameter(final ThreadContext context,
268
+ final Connection connection, final PreparedStatement statement,
269
+ final int index, Object value,
270
+ final IRubyObject column, final int type) throws SQLException {
271
+
272
+ final String columnType = column.callMethod(context, "type").asJavaString();
273
+
274
+ if ( columnType == (Object) "uuid" ) {
275
+ setUUIDParameter(statement, index, value);
276
+ return;
277
+ }
278
+
279
+ if ( columnType == (Object) "json" ) {
280
+ setJsonParameter(context, statement, index, value, column);
281
+ return;
282
+ }
283
+
284
+ if ( columnType == (Object) "tsvector" ) {
285
+ setTsVectorParameter(statement, index, value);
286
+ return;
287
+ }
288
+
289
+ if ( columnType == (Object) "cidr" || columnType == (Object) "inet"
290
+ || columnType == (Object) "macaddr" ) {
291
+ setAddressParameter(context, statement, index, value, column, columnType);
292
+ return;
293
+ }
294
+
295
+ if ( columnType != null && columnType.endsWith("range") ) {
296
+ setRangeParameter(context, statement, index, value, column, columnType);
297
+ return;
298
+ }
299
+
300
+ super.setObjectParameter(context, connection, statement, index, value, column, type);
301
+ }
302
+
303
+ private void setUUIDParameter(
304
+ final PreparedStatement statement, final int index,
305
+ Object value) throws SQLException {
306
+
307
+ if ( value instanceof IRubyObject ) {
308
+ final IRubyObject rubyValue = (IRubyObject) value;
309
+ if ( rubyValue.isNil() ) {
310
+ statement.setNull(index, Types.OTHER); return;
311
+ }
312
+ }
313
+ else if ( value == null ) {
314
+ statement.setNull(index, Types.OTHER); return;
315
+ }
316
+
317
+ final Object uuid = UUID.fromString( value.toString() );
318
+ statement.setObject(index, uuid);
319
+ }
320
+
321
+ private void setJsonParameter(final ThreadContext context,
322
+ final PreparedStatement statement, final int index,
323
+ Object value, final IRubyObject column) throws SQLException {
324
+
325
+ if ( value instanceof IRubyObject ) {
326
+ final IRubyObject rubyValue = (IRubyObject) value;
327
+ if ( rubyValue.isNil() ) {
328
+ statement.setNull(index, Types.OTHER); return;
329
+ }
330
+ }
331
+ else if ( value == null ) {
332
+ statement.setNull(index, Types.OTHER); return;
333
+ }
334
+
335
+ final PGobject pgJson = new PGobject();
336
+ pgJson.setType("json");
337
+ pgJson.setValue(value.toString());
338
+ statement.setObject(index, pgJson);
339
+ }
340
+
341
+ private void setTsVectorParameter(
342
+ final PreparedStatement statement, final int index,
343
+ Object value) throws SQLException {
344
+
345
+ if ( value instanceof IRubyObject ) {
346
+ final IRubyObject rubyValue = (IRubyObject) value;
347
+ if ( rubyValue.isNil() ) {
348
+ statement.setNull(index, Types.OTHER); return;
349
+ }
350
+ }
351
+ else if ( value == null ) {
352
+ statement.setNull(index, Types.OTHER); return;
353
+ }
354
+
355
+ final PGobject pgTsVector = new PGobject();
356
+ pgTsVector.setType("tsvector");
357
+ pgTsVector.setValue(value.toString());
358
+ statement.setObject(index, pgTsVector);
359
+ }
360
+
361
+ private void setAddressParameter(final ThreadContext context,
362
+ final PreparedStatement statement, final int index,
363
+ Object value, final IRubyObject column,
364
+ final String columnType) throws SQLException {
365
+
366
+ if ( value instanceof IRubyObject ) {
367
+ final IRubyObject rubyValue = (IRubyObject) value;
368
+ if ( rubyValue.isNil() ) {
369
+ statement.setNull(index, Types.OTHER); return;
370
+ }
371
+ }
372
+ else if ( value == null ) {
373
+ statement.setNull(index, Types.OTHER); return;
374
+ }
375
+
376
+ final PGobject pgAddress = new PGobject();
377
+ pgAddress.setType(columnType);
378
+ pgAddress.setValue(value.toString());
379
+ statement.setObject(index, pgAddress);
380
+ }
381
+
382
+ private void setRangeParameter(final ThreadContext context,
383
+ final PreparedStatement statement, final int index,
384
+ final Object value, final IRubyObject column,
385
+ final String columnType) throws SQLException {
386
+
387
+ final String rangeValue;
388
+
389
+ if ( value instanceof IRubyObject ) {
390
+ final IRubyObject rubyValue = (IRubyObject) value;
391
+ if ( rubyValue.isNil() ) {
392
+ statement.setNull(index, Types.OTHER); return;
393
+ }
394
+ rangeValue = column.getMetaClass().callMethod(context, "range_to_string", rubyValue).toString();
395
+ }
396
+ else {
397
+ if ( value == null ) {
398
+ statement.setNull(index, Types.OTHER); return;
399
+ }
400
+ rangeValue = value.toString();
401
+ }
402
+
403
+ final Object pgRange;
404
+ if ( columnType == (Object) "daterange" ) {
405
+ pgRange = new DateRangeType(rangeValue);
406
+ }
407
+ else if ( columnType == (Object) "tsrange" ) {
408
+ pgRange = new TsRangeType(rangeValue);
409
+ }
410
+ else if ( columnType == (Object) "tstzrange" ) {
411
+ pgRange = new TstzRangeType(rangeValue);
412
+ }
413
+ else if ( columnType == (Object) "int4range" ) {
414
+ pgRange = new Int4RangeType(rangeValue);
415
+ }
416
+ else if ( columnType == (Object) "int8range" ) {
417
+ pgRange = new Int8RangeType(rangeValue);
418
+ }
419
+ else { // if ( columnType == (Object) "numrange" )
420
+ pgRange = new NumRangeType(rangeValue);
421
+ }
422
+ statement.setObject(index, pgRange);
423
+ }
424
+
425
+ @Override
426
+ protected String resolveArrayBaseTypeName(final ThreadContext context,
427
+ final Object value, final IRubyObject column, final int type) {
428
+ String sqlType = column.callMethod(context, "sql_type").toString();
429
+ if ( sqlType.startsWith("character varying") ) return "text";
430
+ final int index = sqlType.indexOf('('); // e.g. "character varying(255)"
431
+ if ( index > 0 ) sqlType = sqlType.substring(0, index);
432
+ return sqlType;
433
+ }
434
+
435
+ private static final int HSTORE_TYPE = 100000 + 1111;
436
+
437
+ @Override
438
+ protected int jdbcTypeFor(final ThreadContext context, final Ruby runtime,
439
+ final IRubyObject column, final Object value) throws SQLException {
440
+ // NOTE: likely wrong but native adapters handles this thus we should
441
+ // too - used from #table_exists? `binds << [ nil, schema ] if schema`
442
+ if ( column == null || column.isNil() ) return Types.VARCHAR; // assume type == :string
443
+ final int type = super.jdbcTypeFor(context, runtime, column, value);
444
+ /*
445
+ if ( type == Types.OTHER ) {
446
+ final IRubyObject columnType = column.callMethod(context, "type");
447
+ if ( "hstore" == (Object) columnType.asJavaString() ) {
448
+ return HSTORE_TYPE;
449
+ }
450
+ } */
451
+ return type;
452
+ }
453
+
454
+ /**
455
+ * Override jdbcToRuby type conversions to handle infinite timestamps.
456
+ * Handing timestamp off to ruby as string so adapter can perform type
457
+ * conversion to timestamp
458
+ */
459
+ @Override
460
+ protected IRubyObject jdbcToRuby(
461
+ final ThreadContext context, final Ruby runtime,
462
+ final int column, final int type, final ResultSet resultSet)
463
+ throws SQLException {
464
+ switch ( type ) {
465
+ case Types.BIT:
466
+ // we do get BIT for 't' 'f' as well as BIT strings e.g. "0110" :
467
+ final String bits = resultSet.getString(column);
468
+ if ( bits == null ) return runtime.getNil();
469
+ if ( bits.length() > 1 ) {
470
+ return RubyString.newUnicodeString(runtime, bits);
471
+ }
472
+ return booleanToRuby(context, runtime, resultSet, column);
473
+ //case Types.JAVA_OBJECT: case Types.OTHER:
474
+ //return objectToRuby(runtime, resultSet, resultSet.getObject(column));
475
+ }
476
+ return super.jdbcToRuby(context, runtime, column, type, resultSet);
477
+ }
478
+
479
+ @Override
480
+ protected IRubyObject timestampToRuby(final ThreadContext context,
481
+ final Ruby runtime, final ResultSet resultSet, final int column)
482
+ throws SQLException {
483
+ // NOTE: using Timestamp we loose information such as BC :
484
+ // Timestamp: '0001-12-31 22:59:59.0' String: '0001-12-31 22:59:59 BC'
485
+ final String value = resultSet.getString(column);
486
+ if ( value == null ) {
487
+ if ( resultSet.wasNull() ) return runtime.getNil();
488
+ return runtime.newString(); // ""
489
+ }
490
+
491
+ final RubyString strValue = timestampToRubyString(runtime, value.toString());
492
+ if ( rawDateTime != null && rawDateTime.booleanValue() ) return strValue;
493
+
494
+ final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
495
+
496
+ return typeCastFromDatabase(context, adapter, runtime.newSymbol("timestamp"), strValue);
497
+ }
498
+
499
+ @Override
500
+ protected IRubyObject arrayToRuby(final ThreadContext context,
501
+ final Ruby runtime, final ResultSet resultSet, final int column)
502
+ throws SQLException {
503
+ if ( rawArrayType == Boolean.TRUE ) { // pre AR 4.0 compatibility
504
+ return stringToRuby(context, runtime, resultSet, column);
505
+ }
506
+ // NOTE: avoid `finally { array.free(); }` on PostgreSQL due :
507
+ // java.sql.SQLFeatureNotSupportedException:
508
+ // Method org.postgresql.jdbc4.Jdbc4Array.free() is not yet implemented.
509
+ final Array value = resultSet.getArray(column);
510
+
511
+ if ( value == null && resultSet.wasNull() ) return runtime.getNil();
512
+
513
+ final RubyArray array = runtime.newArray();
514
+
515
+ final ResultSet arrayResult = value.getResultSet(); // 1: index, 2: value
516
+ final int baseType = value.getBaseType();
517
+ while ( arrayResult.next() ) {
518
+ array.append( jdbcToRuby(context, runtime, 2, baseType, arrayResult) );
519
+ }
520
+ return array;
521
+ }
522
+
523
+ @Override
524
+ protected IRubyObject objectToRuby(final ThreadContext context,
525
+ final Ruby runtime, final ResultSet resultSet, final int column)
526
+ throws SQLException {
527
+
528
+ final Object object = resultSet.getObject(column);
529
+
530
+ if ( object == null && resultSet.wasNull() ) return runtime.getNil();
531
+
532
+ final Class<?> objectClass = object.getClass();
533
+ if ( objectClass == UUID.class ) {
534
+ return runtime.newString( object.toString() );
535
+ }
536
+
537
+ if ( objectClass == PGInterval.class ) {
538
+ return runtime.newString( formatInterval(object) );
539
+ }
540
+
541
+ if ( object instanceof PGobject ) {
542
+ // PG 9.2 JSON type will be returned here as well
543
+ return runtime.newString( object.toString() );
544
+ }
545
+
546
+ if ( object instanceof Map ) { // hstore
547
+ if ( rawHstoreType == Boolean.TRUE ) {
548
+ return runtime.newString( resultSet.getString(column) );
549
+ }
550
+ // by default we avoid double parsing by driver and than column :
551
+ final RubyHash rubyObject = RubyHash.newHash(runtime);
552
+ rubyObject.putAll((Map) object); // converts keys/values to ruby
553
+ return rubyObject;
554
+ }
555
+
556
+ return JavaUtil.convertJavaToRuby(runtime, object);
557
+ }
558
+
559
+ @Override
560
+ protected TableName extractTableName(
561
+ final Connection connection, String catalog, String schema,
562
+ final String tableName) throws IllegalArgumentException, SQLException {
563
+ // The postgres JDBC driver will default to searching every schema if no
564
+ // schema search path is given. Default to the 'public' schema instead:
565
+ if ( schema == null ) schema = "public";
566
+ return super.extractTableName(connection, catalog, schema, tableName);
567
+ }
568
+
569
+ // NOTE: do not use PG classes in the API so that loading is delayed !
570
+ private String formatInterval(final Object object) {
571
+ final PGInterval interval = (PGInterval) object;
572
+ if ( rawIntervalType ) return interval.getValue();
573
+
574
+ final StringBuilder str = new StringBuilder(32);
575
+
576
+ final int years = interval.getYears();
577
+ if ( years != 0 ) str.append(years).append(" years ");
578
+ final int months = interval.getMonths();
579
+ if ( months != 0 ) str.append(months).append(" months ");
580
+ final int days = interval.getDays();
581
+ if ( days != 0 ) str.append(days).append(" days ");
582
+ final int hours = interval.getHours();
583
+ final int mins = interval.getMinutes();
584
+ final int secs = (int) interval.getSeconds();
585
+ if ( hours != 0 || mins != 0 || secs != 0 ) { // xx:yy:zz if not all 00
586
+ if ( hours < 10 ) str.append('0');
587
+ str.append(hours).append(':');
588
+ if ( mins < 10 ) str.append('0');
589
+ str.append(mins).append(':');
590
+ if ( secs < 10 ) str.append('0');
591
+ str.append(secs);
592
+ }
593
+ else {
594
+ if ( str.length() > 1 ) str.deleteCharAt( str.length() - 1 ); // " " at the end
595
+ }
596
+
597
+ return str.toString();
598
+ }
599
+
600
+ protected static Boolean rawArrayType;
601
+ static {
602
+ final String arrayRaw = System.getProperty("arjdbc.postgresql.array.raw");
603
+ if ( arrayRaw != null ) rawArrayType = Boolean.parseBoolean(arrayRaw);
604
+ }
605
+
606
+ @JRubyMethod(name = "raw_array_type?", meta = true)
607
+ public static IRubyObject useRawArrayType(final ThreadContext context, final IRubyObject self) {
608
+ if ( rawArrayType == null ) return context.getRuntime().getNil();
609
+ return context.getRuntime().newBoolean(rawArrayType);
610
+ }
611
+
612
+ @JRubyMethod(name = "raw_array_type=", meta = true)
613
+ public static IRubyObject setRawArrayType(final IRubyObject self, final IRubyObject value) {
614
+ if ( value instanceof RubyBoolean ) {
615
+ rawArrayType = ((RubyBoolean) value).isTrue() ? Boolean.TRUE : Boolean.FALSE;
616
+ }
617
+ else {
618
+ rawArrayType = value.isNil() ? null : Boolean.TRUE;
619
+ }
620
+ return value;
621
+ }
622
+
623
+ protected static Boolean rawHstoreType;
624
+ static {
625
+ final String hstoreRaw = System.getProperty("arjdbc.postgresql.hstore.raw");
626
+ if ( hstoreRaw != null ) rawHstoreType = Boolean.parseBoolean(hstoreRaw);
627
+ }
628
+
629
+ @JRubyMethod(name = "raw_hstore_type?", meta = true)
630
+ public static IRubyObject useRawHstoreType(final ThreadContext context, final IRubyObject self) {
631
+ if ( rawHstoreType == null ) return context.getRuntime().getNil();
632
+ return context.getRuntime().newBoolean(rawHstoreType);
633
+ }
634
+
635
+ @JRubyMethod(name = "raw_hstore_type=", meta = true)
636
+ public static IRubyObject setRawHstoreType(final IRubyObject self, final IRubyObject value) {
637
+ if ( value instanceof RubyBoolean ) {
638
+ rawHstoreType = ((RubyBoolean) value).isTrue() ? Boolean.TRUE : Boolean.FALSE;
639
+ }
640
+ else {
641
+ rawHstoreType = value.isNil() ? null : Boolean.TRUE;
642
+ }
643
+ return value;
644
+ }
645
+
646
+ // whether to use "raw" interval values off by default - due native adapter compatibilty :
647
+ // RAW values :
648
+ // - 2 years 0 mons 0 days 0 hours 3 mins 0.00 secs
649
+ // - -1 years 0 mons -2 days 0 hours 0 mins 0.00 secs
650
+ // Rails style :
651
+ // - 2 years 00:03:00
652
+ // - -1 years -2 days
653
+ protected static boolean rawIntervalType = Boolean.getBoolean("arjdbc.postgresql.iterval.raw");
654
+
655
+ @JRubyMethod(name = "raw_interval_type?", meta = true)
656
+ public static IRubyObject useRawIntervalType(final ThreadContext context, final IRubyObject self) {
657
+ return context.getRuntime().newBoolean(rawIntervalType);
658
+ }
659
+
660
+ @JRubyMethod(name = "raw_interval_type=", meta = true)
661
+ public static IRubyObject setRawIntervalType(final IRubyObject self, final IRubyObject value) {
662
+ if ( value instanceof RubyBoolean ) {
663
+ rawIntervalType = ((RubyBoolean) value).isTrue();
664
+ }
665
+ else {
666
+ rawIntervalType = ! value.isNil();
667
+ }
668
+ return value;
669
+ }
670
+
671
+ // NOTE: without these custom registered Postgre (driver) types
672
+ // ... we can not set range parameters in prepared statements !
673
+
674
+ public static class DateRangeType extends PGobject {
675
+
676
+ public DateRangeType() {
677
+ setType("daterange");
678
+ }
679
+
680
+ public DateRangeType(final String value) throws SQLException {
681
+ this();
682
+ setValue(value);
683
+ }
684
+
685
+ }
686
+
687
+ public static class TsRangeType extends PGobject {
688
+
689
+ public TsRangeType() {
690
+ setType("tsrange");
691
+ }
692
+
693
+ public TsRangeType(final String value) throws SQLException {
694
+ this();
695
+ setValue(value);
696
+ }
697
+
698
+ }
699
+
700
+ public static class TstzRangeType extends PGobject {
701
+
702
+ public TstzRangeType() {
703
+ setType("tstzrange");
704
+ }
705
+
706
+ public TstzRangeType(final String value) throws SQLException {
707
+ this();
708
+ setValue(value);
709
+ }
710
+
711
+ }
712
+
713
+ public static class Int4RangeType extends PGobject {
714
+
715
+ public Int4RangeType() {
716
+ setType("int4range");
717
+ }
718
+
719
+ public Int4RangeType(final String value) throws SQLException {
720
+ this();
721
+ setValue(value);
722
+ }
723
+
724
+ }
725
+
726
+ public static class Int8RangeType extends PGobject {
727
+
728
+ public Int8RangeType() {
729
+ setType("int8range");
730
+ }
731
+
732
+ public Int8RangeType(final String value) throws SQLException {
733
+ this();
734
+ setValue(value);
735
+ }
736
+
737
+ }
738
+
739
+ public static class NumRangeType extends PGobject {
740
+
741
+ public NumRangeType() {
742
+ setType("numrange");
743
+ }
744
+
745
+ public NumRangeType(final String value) throws SQLException {
746
+ this();
747
+ setValue(value);
748
+ }
749
+
750
+ }
751
+
752
+ }