activerecord-jdbc-alt-adapter 50.3.0-java

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 (198) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.travis.yml +100 -0
  4. data/.yardopts +4 -0
  5. data/CONTRIBUTING.md +50 -0
  6. data/Gemfile +92 -0
  7. data/History.md +1191 -0
  8. data/LICENSE.txt +26 -0
  9. data/README.md +240 -0
  10. data/RUNNING_TESTS.md +127 -0
  11. data/Rakefile +336 -0
  12. data/Rakefile.jdbc +20 -0
  13. data/activerecord-jdbc-adapter.gemspec +55 -0
  14. data/activerecord-jdbc-alt-adapter.gemspec +56 -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/postgresql_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  31. data/lib/activerecord-jdbc-adapter.rb +1 -0
  32. data/lib/arel/visitors/compat.rb +60 -0
  33. data/lib/arel/visitors/db2.rb +137 -0
  34. data/lib/arel/visitors/derby.rb +112 -0
  35. data/lib/arel/visitors/firebird.rb +79 -0
  36. data/lib/arel/visitors/h2.rb +25 -0
  37. data/lib/arel/visitors/hsqldb.rb +32 -0
  38. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  39. data/lib/arel/visitors/sql_server.rb +225 -0
  40. data/lib/arel/visitors/sql_server/ng42.rb +294 -0
  41. data/lib/arel/visitors/sqlserver.rb +214 -0
  42. data/lib/arjdbc.rb +19 -0
  43. data/lib/arjdbc/abstract/connection_management.rb +35 -0
  44. data/lib/arjdbc/abstract/core.rb +74 -0
  45. data/lib/arjdbc/abstract/database_statements.rb +64 -0
  46. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  47. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  48. data/lib/arjdbc/db2.rb +4 -0
  49. data/lib/arjdbc/db2/adapter.rb +789 -0
  50. data/lib/arjdbc/db2/as400.rb +130 -0
  51. data/lib/arjdbc/db2/column.rb +167 -0
  52. data/lib/arjdbc/db2/connection_methods.rb +44 -0
  53. data/lib/arjdbc/derby.rb +3 -0
  54. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  55. data/lib/arjdbc/derby/adapter.rb +540 -0
  56. data/lib/arjdbc/derby/connection_methods.rb +20 -0
  57. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  58. data/lib/arjdbc/discover.rb +104 -0
  59. data/lib/arjdbc/firebird.rb +4 -0
  60. data/lib/arjdbc/firebird/adapter.rb +434 -0
  61. data/lib/arjdbc/firebird/connection_methods.rb +23 -0
  62. data/lib/arjdbc/h2.rb +3 -0
  63. data/lib/arjdbc/h2/adapter.rb +303 -0
  64. data/lib/arjdbc/h2/connection_methods.rb +27 -0
  65. data/lib/arjdbc/hsqldb.rb +3 -0
  66. data/lib/arjdbc/hsqldb/adapter.rb +297 -0
  67. data/lib/arjdbc/hsqldb/connection_methods.rb +28 -0
  68. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  69. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  70. data/lib/arjdbc/informix.rb +5 -0
  71. data/lib/arjdbc/informix/adapter.rb +162 -0
  72. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  73. data/lib/arjdbc/jdbc.rb +59 -0
  74. data/lib/arjdbc/jdbc/adapter.rb +475 -0
  75. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  76. data/lib/arjdbc/jdbc/base_ext.rb +15 -0
  77. data/lib/arjdbc/jdbc/callbacks.rb +53 -0
  78. data/lib/arjdbc/jdbc/column.rb +97 -0
  79. data/lib/arjdbc/jdbc/connection.rb +14 -0
  80. data/lib/arjdbc/jdbc/connection_methods.rb +37 -0
  81. data/lib/arjdbc/jdbc/error.rb +65 -0
  82. data/lib/arjdbc/jdbc/extension.rb +59 -0
  83. data/lib/arjdbc/jdbc/java.rb +13 -0
  84. data/lib/arjdbc/jdbc/railtie.rb +2 -0
  85. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -0
  86. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
  87. data/lib/arjdbc/jdbc/type_cast.rb +166 -0
  88. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  89. data/lib/arjdbc/mssql.rb +7 -0
  90. data/lib/arjdbc/mssql/adapter.rb +384 -0
  91. data/lib/arjdbc/mssql/column.rb +29 -0
  92. data/lib/arjdbc/mssql/connection_methods.rb +79 -0
  93. data/lib/arjdbc/mssql/database_statements.rb +134 -0
  94. data/lib/arjdbc/mssql/errors.rb +6 -0
  95. data/lib/arjdbc/mssql/explain_support.rb +129 -0
  96. data/lib/arjdbc/mssql/extensions.rb +36 -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/old_adapter.rb +804 -0
  100. data/lib/arjdbc/mssql/old_column.rb +200 -0
  101. data/lib/arjdbc/mssql/quoting.rb +101 -0
  102. data/lib/arjdbc/mssql/schema_creation.rb +31 -0
  103. data/lib/arjdbc/mssql/schema_definitions.rb +74 -0
  104. data/lib/arjdbc/mssql/schema_statements.rb +329 -0
  105. data/lib/arjdbc/mssql/transaction.rb +69 -0
  106. data/lib/arjdbc/mssql/types.rb +52 -0
  107. data/lib/arjdbc/mssql/types/binary_types.rb +33 -0
  108. data/lib/arjdbc/mssql/types/date_and_time_types.rb +134 -0
  109. data/lib/arjdbc/mssql/types/deprecated_types.rb +40 -0
  110. data/lib/arjdbc/mssql/types/numeric_types.rb +71 -0
  111. data/lib/arjdbc/mssql/types/string_types.rb +56 -0
  112. data/lib/arjdbc/mssql/utils.rb +66 -0
  113. data/lib/arjdbc/mysql.rb +3 -0
  114. data/lib/arjdbc/mysql/adapter.rb +140 -0
  115. data/lib/arjdbc/mysql/connection_methods.rb +166 -0
  116. data/lib/arjdbc/oracle/adapter.rb +863 -0
  117. data/lib/arjdbc/postgresql.rb +3 -0
  118. data/lib/arjdbc/postgresql/adapter.rb +687 -0
  119. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  120. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  121. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  122. data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
  123. data/lib/arjdbc/postgresql/column.rb +51 -0
  124. data/lib/arjdbc/postgresql/connection_methods.rb +67 -0
  125. data/lib/arjdbc/postgresql/name.rb +24 -0
  126. data/lib/arjdbc/postgresql/oid_types.rb +266 -0
  127. data/lib/arjdbc/railtie.rb +11 -0
  128. data/lib/arjdbc/sqlite3.rb +3 -0
  129. data/lib/arjdbc/sqlite3/adapter.rb +678 -0
  130. data/lib/arjdbc/sqlite3/connection_methods.rb +59 -0
  131. data/lib/arjdbc/sybase.rb +2 -0
  132. data/lib/arjdbc/sybase/adapter.rb +47 -0
  133. data/lib/arjdbc/tasks.rb +13 -0
  134. data/lib/arjdbc/tasks/database_tasks.rb +31 -0
  135. data/lib/arjdbc/tasks/databases.rake +48 -0
  136. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  137. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  138. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  139. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  140. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  141. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -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 +3 -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 +132 -0
  157. data/rakelib/bundler_ext.rb +11 -0
  158. data/rakelib/db.rake +75 -0
  159. data/rakelib/rails.rake +223 -0
  160. data/src/java/arjdbc/ArJdbcModule.java +276 -0
  161. data/src/java/arjdbc/db2/DB2Module.java +76 -0
  162. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +126 -0
  163. data/src/java/arjdbc/derby/DerbyModule.java +178 -0
  164. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +152 -0
  165. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +174 -0
  166. data/src/java/arjdbc/h2/H2Module.java +50 -0
  167. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +85 -0
  168. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +73 -0
  169. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +75 -0
  170. data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
  171. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  172. data/src/java/arjdbc/jdbc/ConnectionFactory.java +45 -0
  173. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
  174. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  175. data/src/java/arjdbc/jdbc/DriverWrapper.java +119 -0
  176. data/src/java/arjdbc/jdbc/JdbcResult.java +130 -0
  177. data/src/java/arjdbc/jdbc/RubyConnectionFactory.java +61 -0
  178. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3979 -0
  179. data/src/java/arjdbc/mssql/MSSQLModule.java +90 -0
  180. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +508 -0
  181. data/src/java/arjdbc/mysql/MySQLModule.java +152 -0
  182. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +294 -0
  183. data/src/java/arjdbc/oracle/OracleModule.java +80 -0
  184. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +455 -0
  185. data/src/java/arjdbc/postgresql/ByteaUtils.java +157 -0
  186. data/src/java/arjdbc/postgresql/PgDateTimeUtils.java +52 -0
  187. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +77 -0
  188. data/src/java/arjdbc/postgresql/PostgreSQLResult.java +192 -0
  189. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +948 -0
  190. data/src/java/arjdbc/sqlite3/SQLite3Module.java +73 -0
  191. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +525 -0
  192. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  193. data/src/java/arjdbc/util/DateTimeUtils.java +699 -0
  194. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  195. data/src/java/arjdbc/util/QuotingUtils.java +137 -0
  196. data/src/java/arjdbc/util/StringCache.java +63 -0
  197. data/src/java/arjdbc/util/StringHelper.java +145 -0
  198. metadata +269 -0
@@ -0,0 +1,948 @@
1
+ /***** BEGIN LICENSE BLOCK *****
2
+ * Copyright (c) 2012-2015 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 arjdbc.jdbc.Callable;
29
+ import arjdbc.jdbc.DriverWrapper;
30
+ import arjdbc.util.DateTimeUtils;
31
+ import arjdbc.util.StringHelper;
32
+
33
+ import java.io.ByteArrayInputStream;
34
+ import java.lang.StringBuilder;
35
+ import java.lang.reflect.InvocationTargetException;
36
+ import java.sql.Connection;
37
+ import java.sql.DatabaseMetaData;
38
+ import java.sql.Date;
39
+ import java.sql.PreparedStatement;
40
+ import java.sql.ResultSet;
41
+ import java.sql.SQLException;
42
+ import java.sql.Timestamp;
43
+ import java.sql.Types;
44
+ import java.util.ArrayList;
45
+ import java.util.Collections;
46
+ import java.util.HashMap;
47
+ import java.util.Map;
48
+ import java.util.UUID;
49
+ import java.util.regex.Pattern;
50
+ import java.util.regex.Matcher;
51
+
52
+ import org.joda.time.DateTime;
53
+ import org.joda.time.DateTimeZone;
54
+ import org.jruby.*;
55
+ import org.jruby.anno.JRubyMethod;
56
+ import org.jruby.exceptions.RaiseException;
57
+ import org.jruby.ext.bigdecimal.RubyBigDecimal;
58
+ import org.jruby.javasupport.JavaUtil;
59
+ import org.jruby.runtime.ObjectAllocator;
60
+ import org.jruby.runtime.ThreadContext;
61
+ import org.jruby.runtime.builtin.IRubyObject;
62
+ import org.jruby.util.ByteList;
63
+
64
+ import org.postgresql.PGConnection;
65
+ import org.postgresql.PGStatement;
66
+ import org.postgresql.geometric.PGbox;
67
+ import org.postgresql.geometric.PGcircle;
68
+ import org.postgresql.geometric.PGline;
69
+ import org.postgresql.geometric.PGlseg;
70
+ import org.postgresql.geometric.PGpath;
71
+ import org.postgresql.geometric.PGpoint;
72
+ import org.postgresql.geometric.PGpolygon;
73
+ import org.postgresql.util.PGInterval;
74
+ import org.postgresql.util.PGobject;
75
+
76
+ /**
77
+ *
78
+ * @author enebo
79
+ */
80
+ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection {
81
+ private static final long serialVersionUID = 7235537759545717760L;
82
+ private static final int HSTORE_TYPE = 100000 + 1111;
83
+ private static final Pattern doubleValuePattern = Pattern.compile("(-?\\d+(?:\\.\\d+)?)");
84
+ private static final Pattern uuidPattern = Pattern.compile("\\{?\\p{XDigit}{4}(?:-?(\\p{XDigit}{4})){7}\\}?"); // Fuzzy match postgres's allowed formats
85
+
86
+ private static final Map<String, Integer> POSTGRES_JDBC_TYPE_FOR = new HashMap<String, Integer>(32, 1);
87
+ static {
88
+ POSTGRES_JDBC_TYPE_FOR.put("bit", Types.OTHER);
89
+ POSTGRES_JDBC_TYPE_FOR.put("bit_varying", Types.OTHER);
90
+ POSTGRES_JDBC_TYPE_FOR.put("box", Types.OTHER);
91
+ POSTGRES_JDBC_TYPE_FOR.put("circle", Types.OTHER);
92
+ POSTGRES_JDBC_TYPE_FOR.put("citext", Types.OTHER);
93
+ POSTGRES_JDBC_TYPE_FOR.put("daterange", Types.OTHER);
94
+ POSTGRES_JDBC_TYPE_FOR.put("hstore", Types.OTHER);
95
+ POSTGRES_JDBC_TYPE_FOR.put("int4range", Types.OTHER);
96
+ POSTGRES_JDBC_TYPE_FOR.put("int8range", Types.OTHER);
97
+ POSTGRES_JDBC_TYPE_FOR.put("interval", Types.OTHER);
98
+ POSTGRES_JDBC_TYPE_FOR.put("json", Types.OTHER);
99
+ POSTGRES_JDBC_TYPE_FOR.put("jsonb", Types.OTHER);
100
+ POSTGRES_JDBC_TYPE_FOR.put("line", Types.OTHER);
101
+ POSTGRES_JDBC_TYPE_FOR.put("lseg", Types.OTHER);
102
+ POSTGRES_JDBC_TYPE_FOR.put("ltree", Types.OTHER);
103
+ POSTGRES_JDBC_TYPE_FOR.put("money", Types.OTHER);
104
+ POSTGRES_JDBC_TYPE_FOR.put("numrange", Types.OTHER);
105
+ POSTGRES_JDBC_TYPE_FOR.put("path", Types.OTHER);
106
+ POSTGRES_JDBC_TYPE_FOR.put("point", Types.OTHER);
107
+ POSTGRES_JDBC_TYPE_FOR.put("polygon", Types.OTHER);
108
+ POSTGRES_JDBC_TYPE_FOR.put("tsrange", Types.OTHER);
109
+ POSTGRES_JDBC_TYPE_FOR.put("tstzrange", Types.OTHER);
110
+ POSTGRES_JDBC_TYPE_FOR.put("tsvector", Types.OTHER);
111
+ POSTGRES_JDBC_TYPE_FOR.put("uuid", Types.OTHER);
112
+ }
113
+
114
+ // Used to wipe trailing 0's from points (3.0, 5.6) -> (3, 5.6)
115
+ private static final Pattern pointCleanerPattern = Pattern.compile("\\.0\\b");
116
+
117
+ private RubyClass resultClass;
118
+
119
+ public PostgreSQLRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
120
+ super(runtime, metaClass);
121
+
122
+ resultClass = getMetaClass().getClass("Result");
123
+ }
124
+
125
+ public static RubyClass createPostgreSQLJdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
126
+ final RubyClass clazz = getConnectionAdapters(runtime).
127
+ defineClassUnder("PostgreSQLJdbcConnection", jdbcConnection, ALLOCATOR);
128
+ clazz.defineAnnotatedMethods(PostgreSQLRubyJdbcConnection.class);
129
+ getConnectionAdapters(runtime).setConstant("PostgresJdbcConnection", clazz); // backwards-compat
130
+ return clazz;
131
+ }
132
+
133
+ public static RubyClass load(final Ruby runtime) {
134
+ RubyClass jdbcConnection = getJdbcConnection(runtime);
135
+ RubyClass postgreSQLConnection = createPostgreSQLJdbcConnectionClass(runtime, jdbcConnection);
136
+ PostgreSQLResult.createPostgreSQLResultClass(runtime, postgreSQLConnection);
137
+ return postgreSQLConnection;
138
+ }
139
+
140
+ protected static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
141
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
142
+ return new PostgreSQLRubyJdbcConnection(runtime, klass);
143
+ }
144
+ };
145
+
146
+ @Override
147
+ protected String buildURL(final ThreadContext context, final IRubyObject url) {
148
+ // (deprecated AR-JDBC specific url) options: disabled with adapter: postgresql
149
+ // since it collides with AR as it likes to use the key for its own purposes :
150
+ // e.g. config[:options] = "-c geqo=off"
151
+ return DriverWrapper.buildURL(url, Collections.EMPTY_MAP);
152
+ }
153
+
154
+ @Override
155
+ protected DriverWrapper newDriverWrapper(final ThreadContext context, final String driver) {
156
+ DriverWrapper driverWrapper = super.newDriverWrapper(context, driver);
157
+
158
+ final java.sql.Driver jdbcDriver = driverWrapper.getDriverInstance();
159
+ if ( jdbcDriver.getClass().getName().startsWith("org.postgresql.") ) {
160
+ try { // public static String getVersion()
161
+ final String version = (String) // "PostgreSQL 9.2 JDBC4 (build 1002)"
162
+ jdbcDriver.getClass().getMethod("getVersion").invoke(null);
163
+ if ( version != null && version.indexOf("JDBC3") >= 0 ) {
164
+ // config[:connection_alive_sql] ||= 'SELECT 1'
165
+ setConfigValueIfNotSet(context, "connection_alive_sql", context.runtime.newString("SELECT 1"));
166
+ }
167
+ }
168
+ catch (NoSuchMethodException e) { }
169
+ catch (SecurityException e) { }
170
+ catch (IllegalAccessException e) { }
171
+ catch (InvocationTargetException e) { }
172
+ }
173
+
174
+ return driverWrapper;
175
+ }
176
+
177
+ @Override
178
+ protected final IRubyObject beginTransaction(final ThreadContext context, final Connection connection,
179
+ final IRubyObject isolation) throws SQLException {
180
+ // NOTE: only reversed order - just to ~ match how Rails does it :
181
+ /* if ( connection.getAutoCommit() ) */ connection.setAutoCommit(false);
182
+ if ( isolation != null ) setTransactionIsolation(context, connection, isolation);
183
+ return context.nil;
184
+ }
185
+
186
+ // storesMixedCaseIdentifiers() return false;
187
+ // storesLowerCaseIdentifiers() return true;
188
+ // storesUpperCaseIdentifiers() return false;
189
+
190
+ @Override
191
+ protected String caseConvertIdentifierForRails(final Connection connection, final String value)
192
+ throws SQLException {
193
+ return value;
194
+ }
195
+
196
+ @Override
197
+ protected String caseConvertIdentifierForJdbc(final Connection connection, final String value)
198
+ throws SQLException {
199
+ return value;
200
+ }
201
+
202
+ @Override
203
+ protected String internedTypeFor(final ThreadContext context, final IRubyObject attribute) throws SQLException {
204
+ RubyClass arrayClass = oidArray(context);
205
+ RubyBasicObject attributeType = (RubyBasicObject) attributeType(context, attribute);
206
+ // The type or its delegate is an OID::Array
207
+ if (arrayClass.isInstance(attributeType) ||
208
+ (attributeType.hasInstanceVariable("@delegate_dc_obj") &&
209
+ arrayClass.isInstance(attributeType.getInstanceVariable("@delegate_dc_obj")))) {
210
+ return "array";
211
+ }
212
+
213
+ return super.internedTypeFor(context, attribute);
214
+ }
215
+
216
+ @JRubyMethod(name = "database_product")
217
+ public IRubyObject database_product(final ThreadContext context) {
218
+ return withConnection(context, new Callable<IRubyObject>() {
219
+ public IRubyObject call(final Connection connection) throws SQLException {
220
+ final DatabaseMetaData metaData = connection.getMetaData();
221
+ return RubyString.newString(context.runtime, metaData.getDatabaseProductName() + ' ' + metaData.getDatabaseProductVersion());
222
+ }
223
+ });
224
+ }
225
+
226
+ private transient RubyClass oidArray; // PostgreSQL::OID::Array
227
+
228
+ private RubyClass oidArray(final ThreadContext context) {
229
+ if (oidArray != null) return oidArray;
230
+ final RubyModule PostgreSQL = (RubyModule) getConnectionAdapters(context.runtime).getConstant("PostgreSQL");
231
+ return oidArray = ((RubyModule) PostgreSQL.getConstantAt("OID")).getClass("Array");
232
+ }
233
+
234
+
235
+ @Override
236
+ protected Connection newConnection() throws RaiseException, SQLException {
237
+ final Connection connection;
238
+ try {
239
+ connection = super.newConnection();
240
+ }
241
+ catch (SQLException ex) {
242
+ if ("3D000".equals(ex.getSQLState())) { // invalid_catalog_name
243
+ // org.postgresql.util.PSQLException: FATAL: database "xxx" does not exist
244
+ throw newNoDatabaseError(ex);
245
+ }
246
+ throw ex;
247
+ }
248
+ final PGConnection pgConnection;
249
+ if ( connection instanceof PGConnection ) {
250
+ pgConnection = (PGConnection) connection;
251
+ }
252
+ else {
253
+ pgConnection = connection.unwrap(PGConnection.class);
254
+ }
255
+ pgConnection.addDataType("daterange", DateRangeType.class);
256
+ pgConnection.addDataType("tsrange", TsRangeType.class);
257
+ pgConnection.addDataType("tstzrange", TstzRangeType.class);
258
+ pgConnection.addDataType("int4range", Int4RangeType.class);
259
+ pgConnection.addDataType("int8range", Int8RangeType.class);
260
+ pgConnection.addDataType("numrange", NumRangeType.class);
261
+ return connection;
262
+ }
263
+
264
+ @Override
265
+ protected PostgreSQLResult mapExecuteResult(final ThreadContext context, final Connection connection,
266
+ final ResultSet resultSet) throws SQLException {
267
+ return PostgreSQLResult.newResult(context, resultClass, this, resultSet);
268
+ }
269
+
270
+ /**
271
+ * Maps a query result set into a <code>ActiveRecord</code> result.
272
+ * @param context
273
+ * @param connection
274
+ * @param resultSet
275
+ * @return <code>ActiveRecord::Result</code>
276
+ * @throws SQLException
277
+ */
278
+ @Override
279
+ protected IRubyObject mapQueryResult(final ThreadContext context, final Connection connection,
280
+ final ResultSet resultSet) throws SQLException {
281
+ return mapExecuteResult(context, connection, resultSet).toARResult(context);
282
+ }
283
+
284
+ @Override
285
+ protected void setArrayParameter(final ThreadContext context,
286
+ final Connection connection, final PreparedStatement statement,
287
+ final int index, final IRubyObject value,
288
+ final IRubyObject attribute, final int type) throws SQLException {
289
+
290
+ final String typeName = resolveArrayBaseTypeName(context, attribute);
291
+ final RubyArray valueForDB = (RubyArray) value.callMethod(context, "values");
292
+
293
+ Object[] values;
294
+ switch (typeName) {
295
+ case "datetime":
296
+ case "timestamp": {
297
+ values = PgDateTimeUtils.timestampStringArray(context, valueForDB);
298
+ break;
299
+ }
300
+ default:
301
+ values = valueForDB.toArray();
302
+ break;
303
+ }
304
+
305
+ statement.setArray(index, connection.createArrayOf(typeName, values));
306
+ }
307
+
308
+ @Override
309
+ protected void setBlobParameter(final ThreadContext context,
310
+ final Connection connection, final PreparedStatement statement,
311
+ final int index, final IRubyObject value,
312
+ final IRubyObject attribute, final int type) throws SQLException {
313
+
314
+ if ( value instanceof RubyIO ) { // IO/File
315
+ statement.setBinaryStream(index, ((RubyIO) value).getInStream());
316
+ }
317
+ else { // should be a RubyString
318
+ final ByteList bytes = value.asString().getByteList();
319
+ statement.setBinaryStream(index,
320
+ new ByteArrayInputStream(bytes.unsafeBytes(), bytes.getBegin(), bytes.getRealSize()),
321
+ bytes.getRealSize() // length
322
+ );
323
+ }
324
+ }
325
+
326
+ @Override // to handle infinity timestamp values
327
+ protected void setTimestampParameter(final ThreadContext context,
328
+ final Connection connection, final PreparedStatement statement,
329
+ final int index, IRubyObject value,
330
+ final IRubyObject attribute, final int type) throws SQLException {
331
+ // PGJDBC uses strings internally anyway, so using Timestamp doesn't do any good
332
+ String tsString = PgDateTimeUtils.timestampValueToString(context, value, null, true);
333
+ statement.setObject(index, tsString, Types.OTHER);
334
+ }
335
+
336
+ private static void setTimestampInfinity(final PreparedStatement statement,
337
+ final int index, final double value) throws SQLException {
338
+ final Timestamp timestamp;
339
+ if ( value < 0 ) {
340
+ timestamp = new Timestamp(PGStatement.DATE_NEGATIVE_INFINITY);
341
+ }
342
+ else {
343
+ timestamp = new Timestamp(PGStatement.DATE_POSITIVE_INFINITY);
344
+ }
345
+
346
+ statement.setTimestamp( index, timestamp );
347
+ }
348
+
349
+ @Override
350
+ protected void setTimeParameter(final ThreadContext context,
351
+ final Connection connection, final PreparedStatement statement,
352
+ final int index, IRubyObject value,
353
+ final IRubyObject attribute, final int type) throws SQLException {
354
+ // to handle more fractional second precision than (default) 59.123 only
355
+ String timeStr = DateTimeUtils.timeString(context, value, getDefaultTimeZone(context), true);
356
+ statement.setObject(index, timeStr, Types.OTHER);
357
+ }
358
+
359
+ @Override
360
+ protected void setDateParameter(final ThreadContext context,
361
+ final Connection connection, final PreparedStatement statement,
362
+ final int index, IRubyObject value,
363
+ final IRubyObject attribute, final int type) throws SQLException {
364
+
365
+ if ( ! "Date".equals(value.getMetaClass().getName()) && value.respondsTo("to_date") ) {
366
+ value = value.callMethod(context, "to_date");
367
+ }
368
+
369
+ // NOTE: assuming Date#to_s does right ...
370
+ statement.setDate(index, Date.valueOf(value.toString()));
371
+ }
372
+
373
+ @Override
374
+ protected void setObjectParameter(final ThreadContext context,
375
+ final Connection connection, final PreparedStatement statement,
376
+ final int index, IRubyObject value,
377
+ final IRubyObject attribute, final int type) throws SQLException {
378
+
379
+ final String columnType = attributeSQLType(context, attribute).asJavaString();
380
+ Double[] pointValues;
381
+
382
+ switch ( columnType ) {
383
+ case "bit":
384
+ case "bit_varying":
385
+ // value should be a ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Bit::Data
386
+ setPGobjectParameter(statement, index, value.toString(), "bit");
387
+ break;
388
+
389
+ case "box":
390
+ pointValues = parseDoubles(value);
391
+ statement.setObject(index, new PGbox(pointValues[0], pointValues[1], pointValues[2], pointValues[3]));
392
+ break;
393
+
394
+ case "circle":
395
+ pointValues = parseDoubles(value);
396
+ statement.setObject(index, new PGcircle(pointValues[0], pointValues[1], pointValues[2]));
397
+ break;
398
+
399
+ case "cidr":
400
+ case "citext":
401
+ case "hstore":
402
+ case "inet":
403
+ case "ltree":
404
+ case "macaddr":
405
+ case "tsvector":
406
+ setPGobjectParameter(statement, index, value, columnType);
407
+ break;
408
+
409
+ case "enum":
410
+ statement.setObject(index, value.toString(), Types.OTHER);
411
+ break;
412
+
413
+ case "interval":
414
+ statement.setObject(index, new PGInterval(value.toString()));
415
+ break;
416
+
417
+ case "json":
418
+ case "jsonb":
419
+ setJsonParameter(context, statement, index, value, columnType);
420
+ break;
421
+
422
+ case "line":
423
+ pointValues = parseDoubles(value);
424
+ if ( pointValues.length == 3 ) {
425
+ statement.setObject(index, new PGline(pointValues[0], pointValues[1], pointValues[2]));
426
+ } else {
427
+ statement.setObject(index, new PGline(pointValues[0], pointValues[1], pointValues[2], pointValues[3]));
428
+ }
429
+ break;
430
+
431
+ case "money":
432
+ if (value instanceof RubyBigDecimal) {
433
+ statement.setBigDecimal(index, ((RubyBigDecimal) value).getValue());
434
+ } else {
435
+ setPGobjectParameter(statement, index, value, columnType);
436
+ }
437
+ break;
438
+
439
+ case "lseg":
440
+ pointValues = parseDoubles(value);
441
+ statement.setObject(index, new PGlseg(pointValues[0], pointValues[1], pointValues[2], pointValues[3]));
442
+ break;
443
+
444
+ case "path":
445
+ // If the value starts with "[" it is open, otherwise postgres treats it as a closed path
446
+ statement.setObject(index, new PGpath((PGpoint[]) convertToPoints(parseDoubles(value)), value.toString().startsWith("[")));
447
+ break;
448
+
449
+ case "point":
450
+ pointValues = parseDoubles(value);
451
+ statement.setObject(index, new PGpoint(pointValues[0], pointValues[1]));
452
+ break;
453
+
454
+ case "polygon":
455
+ statement.setObject(index, new PGpolygon((PGpoint[]) convertToPoints(parseDoubles(value))));
456
+ break;
457
+
458
+ case "uuid":
459
+ setUUIDParameter(statement, index, value);
460
+ break;
461
+
462
+ default:
463
+ if (columnType.endsWith("range")) {
464
+ setRangeParameter(context, statement, index, value, columnType);
465
+ } else {
466
+ setPGobjectParameter(statement, index, value, columnType);
467
+ }
468
+ }
469
+ }
470
+
471
+ // The tests won't start if this returns PGpoint[]
472
+ // it fails with a runtime error: "NativeException: java.lang.reflect.InvocationTargetException: [Lorg/postgresql/geometric/PGpoint"
473
+ private Object[] convertToPoints(Double[] values) throws SQLException {
474
+ PGpoint[] points = new PGpoint[values.length / 2];
475
+
476
+ for ( int i = 0; i < values.length; i += 2 ) {
477
+ points[i / 2] = new PGpoint(values[i], values[i + 1]);
478
+ }
479
+
480
+ return points;
481
+ }
482
+
483
+ private static Double[] parseDoubles(IRubyObject value) {
484
+ Matcher matches = doubleValuePattern.matcher(value.toString());
485
+ ArrayList<Double> doubles = new ArrayList<Double>(4); // Paths and polygons may be larger but this covers points/circles/boxes/line segments
486
+
487
+ while ( matches.find() ) {
488
+ doubles.add(Double.parseDouble(matches.group()));
489
+ }
490
+
491
+ return doubles.toArray(new Double[doubles.size()]);
492
+ }
493
+
494
+ private void setJsonParameter(final ThreadContext context,
495
+ final PreparedStatement statement, final int index,
496
+ final IRubyObject value, final String columnType) throws SQLException {
497
+
498
+ final PGobject pgJson = new PGobject();
499
+ pgJson.setType(columnType);
500
+ pgJson.setValue(value.toString());
501
+ statement.setObject(index, pgJson);
502
+ }
503
+
504
+ private void setPGobjectParameter(final PreparedStatement statement, final int index,
505
+ final Object value, final String columnType) throws SQLException {
506
+
507
+ final PGobject param = new PGobject();
508
+ param.setType(columnType);
509
+ param.setValue(value.toString());
510
+ statement.setObject(index, param);
511
+ }
512
+
513
+ private void setRangeParameter(final ThreadContext context,
514
+ final PreparedStatement statement, final int index,
515
+ final IRubyObject value, final String columnType) throws SQLException {
516
+
517
+ final String rangeValue = value.toString();
518
+ final Object pgRange;
519
+
520
+ switch ( columnType ) {
521
+ case "daterange":
522
+ pgRange = new DateRangeType(rangeValue);
523
+ break;
524
+ case "tsrange":
525
+ pgRange = new TsRangeType(rangeValue);
526
+ break;
527
+ case "tstzrange":
528
+ pgRange = new TstzRangeType(rangeValue);
529
+ break;
530
+ case "int4range":
531
+ pgRange = new Int4RangeType(rangeValue);
532
+ break;
533
+ case "int8range":
534
+ pgRange = new Int8RangeType(rangeValue);
535
+ break;
536
+ default:
537
+ pgRange = new NumRangeType(rangeValue);
538
+ }
539
+
540
+ statement.setObject(index, pgRange);
541
+ }
542
+
543
+ @Override
544
+ protected void setStringParameter(final ThreadContext context,
545
+ final Connection connection, final PreparedStatement statement,
546
+ final int index, final IRubyObject value,
547
+ final IRubyObject attribute, final int type) throws SQLException {
548
+
549
+ if ( attributeSQLType(context, attribute) == context.nil ) {
550
+ /*
551
+ We have to check for a uuid here because in some cases
552
+ (for example, when doing "exists?" checks, or with legacy binds)
553
+ ActiveRecord doesn't send us the actual type of the attribute
554
+ and Postgres won't compare a uuid column with a string
555
+ */
556
+ final String uuid = value.toString();
557
+ int length = uuid.length();
558
+
559
+ // Checking the length so we don't have the overhead of the regex unless it "looks" like a UUID
560
+ if (length >= 32 && length < 40 && uuidPattern.matcher(uuid).matches()) {
561
+ setUUIDParameter(statement, index, uuid);
562
+ return;
563
+ }
564
+ }
565
+
566
+ super.setStringParameter(context, connection, statement, index, value, attribute, type);
567
+ }
568
+
569
+
570
+ private void setUUIDParameter(final PreparedStatement statement,
571
+ final int index, final IRubyObject value) throws SQLException {
572
+ setUUIDParameter(statement, index, value.toString());
573
+ }
574
+
575
+ private void setUUIDParameter(final PreparedStatement statement, final int index, String uuid) throws SQLException {
576
+
577
+ if (uuid.length() != 36) { // Assume its a non-standard format
578
+
579
+ /*
580
+ * Postgres supports a bunch of formats that aren't valid uuids, so we do too...
581
+ * A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11
582
+ * {a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}
583
+ * a0eebc999c0b4ef8bb6d6bb9bd380a11
584
+ * a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
585
+ * {a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}
586
+ */
587
+
588
+ if (uuid.length() == 38 && uuid.charAt(0) == '{') {
589
+
590
+ // We got the {a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11} version so save some processing
591
+ uuid = uuid.substring(1, 37);
592
+
593
+ } else {
594
+
595
+ int valueIndex = 0;
596
+ int newUUIDIndex = 0;
597
+ char[] newUUIDChars = new char[36];
598
+
599
+ if (uuid.charAt(0) == '{') {
600
+ // Skip '{'
601
+ valueIndex++;
602
+ }
603
+
604
+ while (newUUIDIndex < 36) { // If we don't hit this before running out of characters it is an invalid UUID
605
+
606
+ char currentChar = uuid.charAt(valueIndex);
607
+
608
+ // Copy anything other than dashes
609
+ if (currentChar != '-') {
610
+ newUUIDChars[newUUIDIndex] = currentChar;
611
+ newUUIDIndex++;
612
+
613
+ // Insert dashes where appropriate
614
+ if(newUUIDIndex == 8 || newUUIDIndex == 13 || newUUIDIndex == 18 || newUUIDIndex == 23) {
615
+ newUUIDChars[newUUIDIndex] = '-';
616
+ newUUIDIndex++;
617
+ }
618
+ }
619
+
620
+ valueIndex++;
621
+ }
622
+
623
+ uuid = new String(newUUIDChars);
624
+
625
+ }
626
+ }
627
+
628
+ statement.setObject(index, UUID.fromString(uuid));
629
+ }
630
+
631
+ @Override
632
+ protected Integer jdbcTypeFor(final String type) {
633
+
634
+ Integer typeValue = POSTGRES_JDBC_TYPE_FOR.get(type);
635
+
636
+ if ( typeValue != null ) {
637
+ return typeValue;
638
+ }
639
+
640
+ return super.jdbcTypeFor(type);
641
+ }
642
+
643
+ @Override
644
+ protected TableName extractTableName(
645
+ final Connection connection, String catalog, String schema,
646
+ final String tableName) throws IllegalArgumentException, SQLException {
647
+ // The postgres JDBC driver will default to searching every schema if no
648
+ // schema search path is given. Default to the 'public' schema instead:
649
+ if ( schema == null ) schema = "public";
650
+ return super.extractTableName(connection, catalog, schema, tableName);
651
+ }
652
+
653
+ /**
654
+ * Determines if this field is multiple bits or a single bit (or t/f),
655
+ * if there are multiple bits they are turned into a string, if there
656
+ * is only one it is assumed to be a boolean value
657
+ * @param context current thread context
658
+ * @param resultSet the jdbc result set to pull the value from
659
+ * @param index the index of the column to convert
660
+ * @return RubyNil if NULL or RubyString of bits or RubyBoolean for a boolean value
661
+ * @throws SQLException if it failes to retrieve the value from the result set
662
+ */
663
+ @Override
664
+ protected IRubyObject bitToRuby(ThreadContext context, Ruby runtime, ResultSet resultSet, int index) throws SQLException {
665
+ String bits = resultSet.getString(index);
666
+
667
+ if (bits == null) return context.nil;
668
+ if (bits.length() > 1) return RubyString.newUnicodeString(context.runtime, bits);
669
+
670
+ // We assume it is a boolean value if it doesn't have a length
671
+ return booleanToRuby(context, runtime, resultSet, index);
672
+ }
673
+
674
+ /**
675
+ * Converts a JDBC date object to a Ruby date by parsing the string so we can handle edge cases
676
+ * @param context current thread context
677
+ * @param resultSet the jdbc result set to pull the value from
678
+ * @param index the index of the column to convert
679
+ * @return RubyNil if NULL or RubyDate if there is a value
680
+ * @throws SQLException if it failes to retrieve the value from the result set
681
+ */
682
+ @Override
683
+ protected IRubyObject dateToRuby(ThreadContext context, Ruby runtime, ResultSet resultSet, int index) throws SQLException {
684
+ // NOTE: PostgreSQL adapter under MRI using pg gem returns UTC-d Date/Time values
685
+ final String value = resultSet.getString(index);
686
+
687
+ return value == null ? context.nil : DateTimeUtils.parseDate(context, value, getDefaultTimeZone(context));
688
+ }
689
+
690
+
691
+ /**
692
+ * Detects PG specific types and converts them to their Ruby equivalents
693
+ * @param context current thread context
694
+ * @param resultSet the jdbc result set to pull the value from
695
+ * @param index the index of the column to convert
696
+ * @return RubyNil if NULL or RubyHash/RubyString/RubyObject
697
+ * @throws SQLException if it failes to retrieve the value from the result set
698
+ */
699
+ @Override
700
+ protected IRubyObject objectToRuby(ThreadContext context, Ruby runtime, ResultSet resultSet, int index) throws SQLException {
701
+ final Object object = resultSet.getObject(index);
702
+
703
+ if (object == null) return context.nil;
704
+
705
+ final Class<?> objectClass = object.getClass();
706
+
707
+ if (objectClass == UUID.class) return runtime.newString(object.toString());
708
+
709
+ if (object instanceof PGobject) {
710
+ if (objectClass == PGInterval.class) return runtime.newString(formatInterval(object));
711
+
712
+ if (objectClass == PGbox.class || objectClass == PGcircle.class ||
713
+ objectClass == PGline.class || objectClass == PGlseg.class ||
714
+ objectClass == PGpath.class || objectClass == PGpoint.class ||
715
+ objectClass == PGpolygon.class ) {
716
+ // AR 5.0+ expects that points don't have the '.0' if it is an integer
717
+ return runtime.newString(pointCleanerPattern.matcher(object.toString()).replaceAll(""));
718
+ }
719
+
720
+ // PG 9.2 JSON type will be returned here as well
721
+ return runtime.newString(object.toString());
722
+ }
723
+
724
+ if (object instanceof Map) { // hstore
725
+ // by default we avoid double parsing by driver and then column :
726
+ final RubyHash rubyObject = RubyHash.newHash(context.runtime);
727
+ rubyObject.putAll((Map) object); // converts keys/values to ruby
728
+ return rubyObject;
729
+ }
730
+
731
+ return JavaUtil.convertJavaToRuby(runtime, object);
732
+ }
733
+
734
+ /**
735
+ * Override character stream handling to be read as bytes
736
+ * @param context current thread context
737
+ * @param runtime the Ruby runtime
738
+ * @param resultSet the jdbc result set to pull the value from
739
+ * @param index the index of the column to convert
740
+ * @return RubyNil if NULL or RubyString if there is a value
741
+ * @throws SQLException if it failes to retrieve the value from the result set
742
+ */
743
+ @Override
744
+ protected IRubyObject readerToRuby(ThreadContext context, Ruby runtime,
745
+ ResultSet resultSet, int index) throws SQLException {
746
+ return stringToRuby(context, runtime, resultSet, index);
747
+ }
748
+
749
+ /**
750
+ * Converts a string column into a Ruby string by pulling the raw bytes from the column and
751
+ * turning them into a string using the default encoding
752
+ * @param context current thread context
753
+ * @param runtime the Ruby runtime
754
+ * @param resultSet the jdbc result set to pull the value from
755
+ * @param index the index of the column to convert
756
+ * @return RubyNil if NULL or RubyString if there is a value
757
+ * @throws SQLException if it failes to retrieve the value from the result set
758
+ */
759
+ @Override
760
+ protected IRubyObject stringToRuby(ThreadContext context, Ruby runtime, ResultSet resultSet, int index) throws SQLException {
761
+ final byte[] value = resultSet.getBytes(index);
762
+
763
+ return value == null ? context.nil : StringHelper.newDefaultInternalString(context.runtime, value);
764
+ }
765
+
766
+ /**
767
+ * Converts a JDBC time object to a Ruby time by parsing it as a string
768
+ * @param context current thread context
769
+ * @param runtime the Ruby runtime
770
+ * @param resultSet the jdbc result set to pull the value from
771
+ * @param column the index of the column to convert
772
+ * @return RubyNil if NULL or RubyDate if there is a value
773
+ * @throws SQLException if it failes to retrieve the value from the result set
774
+ */
775
+ @Override
776
+ protected IRubyObject timeToRuby(ThreadContext context, Ruby runtime, ResultSet resultSet, int column) throws SQLException {
777
+ final String value = resultSet.getString(column); // Using resultSet.getTimestamp(column) only gets .999 (3) precision
778
+
779
+ return value == null ? context.nil : DateTimeUtils.parseTime(context, value, getDefaultTimeZone(context));
780
+ }
781
+
782
+ /**
783
+ * Converts a JDBC timestamp object to a Ruby time by parsing it as a string
784
+ * @param context current thread context
785
+ * @param runtime the Ruby runtime
786
+ * @param resultSet the jdbc result set to pull the value from
787
+ * @param column the index of the column to convert
788
+ * @return RubyNil if NULL or RubyDate if there is a value
789
+ * @throws SQLException if it failes to retrieve the value from the result set
790
+ */
791
+ @Override
792
+ protected IRubyObject timestampToRuby(ThreadContext context, Ruby runtime, ResultSet resultSet,
793
+ int column) throws SQLException {
794
+ // NOTE: using Timestamp we loose information such as BC :
795
+ // Timestamp: '0001-12-31 22:59:59.0' String: '0001-12-31 22:59:59 BC'
796
+ final String value = resultSet.getString(column);
797
+
798
+ if (value == null) return context.nil;
799
+
800
+ final int len = value.length();
801
+ if (len < 10 && value.charAt(len - 1) == 'y') { // infinity / -infinity
802
+ IRubyObject infinity = parseInfinity(context.runtime, value);
803
+
804
+ if (infinity != null) return infinity;
805
+ }
806
+
807
+ // handles '0001-01-01 23:59:59 BC'
808
+ return DateTimeUtils.parseDateTime(context, value, getDefaultTimeZone(context));
809
+ }
810
+
811
+ private IRubyObject parseInfinity(final Ruby runtime, final String value) {
812
+ if ("infinity".equals(value)) return RubyFloat.newFloat(runtime, RubyFloat.INFINITY);
813
+ if ("-infinity".equals(value)) return RubyFloat.newFloat(runtime, -RubyFloat.INFINITY);
814
+
815
+ return null;
816
+ }
817
+
818
+ // NOTE: do not use PG classes in the API so that loading is delayed !
819
+ private static String formatInterval(final Object object) {
820
+ final PGInterval interval = (PGInterval) object;
821
+ final StringBuilder str = new StringBuilder(32);
822
+
823
+ final int years = interval.getYears();
824
+ if (years != 0) str.append(years).append(" years ");
825
+
826
+ final int months = interval.getMonths();
827
+ if (months != 0) str.append(months).append(" months ");
828
+
829
+ final int days = interval.getDays();
830
+ if (days != 0) str.append(days).append(" days ");
831
+
832
+ final int hours = interval.getHours();
833
+ final int mins = interval.getMinutes();
834
+ final int secs = (int) interval.getSeconds();
835
+ if (hours != 0 || mins != 0 || secs != 0) { // xx:yy:zz if not all 00
836
+ if (hours < 10) str.append('0');
837
+
838
+ str.append(hours).append(':');
839
+
840
+ if (mins < 10) str.append('0');
841
+
842
+ str.append(mins).append(':');
843
+
844
+ if (secs < 10) str.append('0');
845
+
846
+ str.append(secs);
847
+
848
+ } else if (str.length() > 1) {
849
+ str.deleteCharAt(str.length() - 1); // " " at the end
850
+ }
851
+
852
+ return str.toString();
853
+ }
854
+
855
+ // NOTE: without these custom registered Postgre (driver) types
856
+ // ... we can not set range parameters in prepared statements !
857
+
858
+ public static class DateRangeType extends PGobject {
859
+
860
+ private static final long serialVersionUID = -5378414736244196691L;
861
+
862
+ public DateRangeType() {
863
+ setType("daterange");
864
+ }
865
+
866
+ public DateRangeType(final String value) throws SQLException {
867
+ this();
868
+ setValue(value);
869
+ }
870
+
871
+ }
872
+
873
+ public static class TsRangeType extends PGobject {
874
+
875
+ private static final long serialVersionUID = -2991390995527988409L;
876
+
877
+ public TsRangeType() {
878
+ setType("tsrange");
879
+ }
880
+
881
+ public TsRangeType(final String value) throws SQLException {
882
+ this();
883
+ setValue(value);
884
+ }
885
+
886
+ }
887
+
888
+ public static class TstzRangeType extends PGobject {
889
+
890
+ private static final long serialVersionUID = 6492535255861743334L;
891
+
892
+ public TstzRangeType() {
893
+ setType("tstzrange");
894
+ }
895
+
896
+ public TstzRangeType(final String value) throws SQLException {
897
+ this();
898
+ setValue(value);
899
+ }
900
+
901
+ }
902
+
903
+ public static class Int4RangeType extends PGobject {
904
+
905
+ private static final long serialVersionUID = 4490562039665289763L;
906
+
907
+ public Int4RangeType() {
908
+ setType("int4range");
909
+ }
910
+
911
+ public Int4RangeType(final String value) throws SQLException {
912
+ this();
913
+ setValue(value);
914
+ }
915
+
916
+ }
917
+
918
+ public static class Int8RangeType extends PGobject {
919
+
920
+ private static final long serialVersionUID = -1458706215346897102L;
921
+
922
+ public Int8RangeType() {
923
+ setType("int8range");
924
+ }
925
+
926
+ public Int8RangeType(final String value) throws SQLException {
927
+ this();
928
+ setValue(value);
929
+ }
930
+
931
+ }
932
+
933
+ public static class NumRangeType extends PGobject {
934
+
935
+ private static final long serialVersionUID = 5892509252900362510L;
936
+
937
+ public NumRangeType() {
938
+ setType("numrange");
939
+ }
940
+
941
+ public NumRangeType(final String value) throws SQLException {
942
+ this();
943
+ setValue(value);
944
+ }
945
+
946
+ }
947
+
948
+ }