activerecord-jdbc-adapter 60.4-java → 61.2-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.
@@ -86,6 +86,7 @@ import org.jruby.anno.JRubyMethod;
86
86
  import org.jruby.exceptions.RaiseException;
87
87
  import org.jruby.ext.bigdecimal.RubyBigDecimal;
88
88
  import org.jruby.ext.date.RubyDate;
89
+ import org.jruby.ext.date.RubyDateTime;
89
90
  import org.jruby.javasupport.JavaEmbedUtils;
90
91
  import org.jruby.javasupport.JavaUtil;
91
92
  import org.jruby.runtime.Block;
@@ -124,6 +125,7 @@ public class RubyJdbcConnection extends RubyObject {
124
125
  private IRubyObject config;
125
126
  private IRubyObject adapter; // the AbstractAdapter instance we belong to
126
127
  private volatile boolean connected = true;
128
+ private RubyClass attributeClass;
127
129
 
128
130
  private boolean lazy = false; // final once set on initialize
129
131
  private boolean jndi; // final once set on initialize
@@ -132,6 +134,7 @@ public class RubyJdbcConnection extends RubyObject {
132
134
 
133
135
  protected RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
134
136
  super(runtime, metaClass);
137
+ attributeClass = runtime.getModule("ActiveModel").getClass("Attribute");
135
138
  }
136
139
 
137
140
  private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
@@ -359,7 +362,7 @@ public class RubyJdbcConnection extends RubyObject {
359
362
  if ( ! connection.getAutoCommit() ) {
360
363
  try {
361
364
  connection.commit();
362
- resetSavepoints(context); // if any
365
+ resetSavepoints(context, connection); // if any
363
366
  return context.runtime.newBoolean(true);
364
367
  }
365
368
  finally {
@@ -380,7 +383,7 @@ public class RubyJdbcConnection extends RubyObject {
380
383
  if ( ! connection.getAutoCommit() ) {
381
384
  try {
382
385
  connection.rollback();
383
- resetSavepoints(context); // if any
386
+ resetSavepoints(context, connection); // if any
384
387
  return context.tru;
385
388
  } finally {
386
389
  connection.setAutoCommit(true);
@@ -516,7 +519,7 @@ public class RubyJdbcConnection extends RubyObject {
516
519
  return null;
517
520
  }
518
521
 
519
- protected boolean resetSavepoints(final ThreadContext context) {
522
+ protected boolean resetSavepoints(final ThreadContext context, final Connection connection) throws SQLException {
520
523
  if ( hasInternalVariable("savepoints") ) {
521
524
  removeInternalVariable("savepoints");
522
525
  return true;
@@ -610,11 +613,7 @@ public class RubyJdbcConnection extends RubyObject {
610
613
  return convertJavaToRuby( connection.unwrap(Connection.class) );
611
614
  }
612
615
  }
613
- catch (AbstractMethodError e) {
614
- debugStackTrace(context, e);
615
- warn(context, "driver/pool connection does not support unwrapping: " + e);
616
- }
617
- catch (SQLException e) {
616
+ catch (AbstractMethodError | SQLException e) {
618
617
  debugStackTrace(context, e);
619
618
  warn(context, "driver/pool connection does not support unwrapping: " + e);
620
619
  }
@@ -860,27 +859,25 @@ public class RubyJdbcConnection extends RubyObject {
860
859
  */
861
860
  @JRubyMethod(name = "execute_insert_pk", required = 2)
862
861
  public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject pk) {
863
- return withConnection(context, new Callable<IRubyObject>() {
864
- public IRubyObject call(final Connection connection) throws SQLException {
865
- Statement statement = null;
866
- final String query = sqlString(sql);
867
- try {
868
-
869
- statement = createStatement(context, connection);
862
+ return withConnection(context, connection -> {
863
+ Statement statement = null;
864
+ final String query = sqlString(sql);
865
+ try {
870
866
 
871
- if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
872
- statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
873
- } else {
874
- statement.executeUpdate(query, createStatementPk(pk));
875
- }
867
+ statement = createStatement(context, connection);
876
868
 
877
- return mapGeneratedKeys(context, connection, statement);
878
- } catch (final SQLException e) {
879
- debugErrorSQL(context, query);
880
- throw e;
881
- } finally {
882
- close(statement);
869
+ if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
870
+ statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
871
+ } else {
872
+ statement.executeUpdate(query, createStatementPk(pk));
883
873
  }
874
+
875
+ return mapGeneratedKeys(context, connection, statement);
876
+ } catch (final SQLException e) {
877
+ debugErrorSQL(context, query);
878
+ throw e;
879
+ } finally {
880
+ close(statement);
884
881
  }
885
882
  });
886
883
  }
@@ -903,26 +900,24 @@ public class RubyJdbcConnection extends RubyObject {
903
900
  @JRubyMethod(name = "execute_insert_pk", required = 3)
904
901
  public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject binds,
905
902
  final IRubyObject pk) {
906
- return withConnection(context, new Callable<IRubyObject>() {
907
- public IRubyObject call(final Connection connection) throws SQLException {
908
- PreparedStatement statement = null;
909
- final String query = sqlString(sql);
910
- try {
911
- if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
912
- statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
913
- } else {
914
- statement = connection.prepareStatement(query, createStatementPk(pk));
915
- }
916
-
917
- setStatementParameters(context, connection, statement, (RubyArray) binds);
918
- statement.executeUpdate();
919
- return mapGeneratedKeys(context, connection, statement);
920
- } catch (final SQLException e) {
921
- debugErrorSQL(context, query);
922
- throw e;
923
- } finally {
924
- close(statement);
903
+ return withConnection(context, connection -> {
904
+ PreparedStatement statement = null;
905
+ final String query = sqlString(sql);
906
+ try {
907
+ if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
908
+ statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
909
+ } else {
910
+ statement = connection.prepareStatement(query, createStatementPk(pk));
925
911
  }
912
+
913
+ setStatementParameters(context, connection, statement, (RubyArray) binds);
914
+ statement.executeUpdate();
915
+ return mapGeneratedKeys(context, connection, statement);
916
+ } catch (final SQLException e) {
917
+ debugErrorSQL(context, query);
918
+ throw e;
919
+ } finally {
920
+ close(statement);
926
921
  }
927
922
  });
928
923
  }
@@ -1012,12 +1007,12 @@ public class RubyJdbcConnection extends RubyObject {
1012
1007
  binds = null;
1013
1008
  } else { // (sql, binds)
1014
1009
  maxRows = 0;
1015
- binds = (RubyArray) TypeConverter.checkArrayType(args[1]);
1010
+ binds = (RubyArray) TypeConverter.checkArrayType(context, args[1]);
1016
1011
  }
1017
1012
  break;
1018
1013
  case 3: // (sql, max_rows, binds)
1019
1014
  maxRows = RubyNumeric.fix2int(args[1]);
1020
- binds = (RubyArray) TypeConverter.checkArrayType(args[2]);
1015
+ binds = (RubyArray) TypeConverter.checkArrayType(context, args[2]);
1021
1016
  break;
1022
1017
  default: // (sql) 1-arg
1023
1018
  maxRows = 0;
@@ -1106,6 +1101,28 @@ public class RubyJdbcConnection extends RubyObject {
1106
1101
  });
1107
1102
  }
1108
1103
 
1104
+ @JRubyMethod(required = 1)
1105
+ public IRubyObject get_first_value(final ThreadContext context, final IRubyObject sql) {
1106
+ return withConnection(context, connection -> {
1107
+ Statement statement = null;
1108
+ final String query = sqlString(sql);
1109
+ try {
1110
+ statement = createStatement(context, connection);
1111
+ statement.execute(query);
1112
+ ResultSet rs = statement.getResultSet();
1113
+ if (rs == null || !rs.next()) return context.nil;
1114
+
1115
+ return jdbcToRuby(context, context.getRuntime(), 1, rs.getMetaData().getColumnType(1), rs);
1116
+
1117
+ } catch (final SQLException e) {
1118
+ debugErrorSQL(context, query);
1119
+ throw e;
1120
+ } finally {
1121
+ close(statement);
1122
+ }
1123
+ });
1124
+ }
1125
+
1109
1126
  /**
1110
1127
  * Prepares a query, returns a wrapped PreparedStatement. This takes care of exception wrapping
1111
1128
  * @param context which context this method is executing on.
@@ -2402,9 +2419,16 @@ public class RubyJdbcConnection extends RubyObject {
2402
2419
  final Connection connection, final PreparedStatement statement,
2403
2420
  final int index, IRubyObject attribute) throws SQLException {
2404
2421
 
2405
- //debugMessage(context, attribute);
2406
- final int type = jdbcTypeForAttribute(context, attribute);
2407
- IRubyObject value = valueForDatabase(context, attribute);
2422
+ final IRubyObject value;
2423
+ final int type;
2424
+
2425
+ if (attributeClass.isInstance(attribute)) {
2426
+ type = jdbcTypeForAttribute(context, attribute);
2427
+ value = valueForDatabase(context, attribute);
2428
+ } else {
2429
+ type = jdbcTypeForPrimitiveAttribute(context, attribute);
2430
+ value = attribute;
2431
+ }
2408
2432
 
2409
2433
  // All the set methods were calling this first so save a method call in the nil case
2410
2434
  if ( value == context.nil ) {
@@ -2520,6 +2544,34 @@ public class RubyJdbcConnection extends RubyObject {
2520
2544
  return Types.OTHER; // -1 as well as 0 are used in Types
2521
2545
  }
2522
2546
 
2547
+ protected String internedTypeForPrimitive(final ThreadContext context, final IRubyObject value) throws SQLException {
2548
+ if (value instanceof RubyString) {
2549
+ return "string";
2550
+ }
2551
+ if (value instanceof RubyInteger) {
2552
+ return "integer";
2553
+ }
2554
+ if (value instanceof RubyNumeric) {
2555
+ return "float";
2556
+ }
2557
+ if (value instanceof RubyTime || value instanceof RubyDateTime) {
2558
+ return "timestamp";
2559
+ }
2560
+ if (value instanceof RubyDate) {
2561
+ return "date";
2562
+ }
2563
+ if (value instanceof RubyBoolean) {
2564
+ return "boolean";
2565
+ }
2566
+ return "string";
2567
+ }
2568
+
2569
+ protected Integer jdbcTypeForPrimitiveAttribute(final ThreadContext context,
2570
+ final IRubyObject attribute) throws SQLException {
2571
+ final String internedType = internedTypeForPrimitive(context, attribute);
2572
+ return jdbcTypeFor(internedType);
2573
+ }
2574
+
2523
2575
  protected Integer jdbcTypeFor(final String type) {
2524
2576
  return JDBC_TYPE_FOR.get(type);
2525
2577
  }
@@ -2531,7 +2583,9 @@ public class RubyJdbcConnection extends RubyObject {
2531
2583
  }
2532
2584
 
2533
2585
  protected static IRubyObject attributeSQLType(final ThreadContext context, final IRubyObject attribute) {
2534
- return attributeType(context, attribute).callMethod(context, "type");
2586
+ final IRubyObject type = attributeType(context, attribute);
2587
+ if (type != null) return type.callMethod(context, "type");
2588
+ return context.nil;
2535
2589
  }
2536
2590
 
2537
2591
  private final CachingCallSite value_site = new FunctionalCachingCallSite("value"); // AR::Attribute#value
@@ -2544,23 +2598,7 @@ public class RubyJdbcConnection extends RubyObject {
2544
2598
 
2545
2599
  final IRubyObject value = value_site.call(context, attribute, attribute);
2546
2600
 
2547
- if (value instanceof RubyInteger) {
2548
- return "integer";
2549
- }
2550
-
2551
- if (value instanceof RubyNumeric) {
2552
- return "float";
2553
- }
2554
-
2555
- if (value instanceof RubyTime) {
2556
- return "timestamp";
2557
- }
2558
-
2559
- if (value instanceof RubyBoolean) {
2560
- return "boolean";
2561
- }
2562
-
2563
- return "string";
2601
+ return internedTypeForPrimitive(context, value);
2564
2602
  }
2565
2603
 
2566
2604
  protected final RubyTime timeInDefaultTimeZone(final ThreadContext context, final IRubyObject value) {
@@ -197,9 +197,9 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
197
197
  RubyClass arrayClass = oidArray(context);
198
198
  RubyBasicObject attributeType = (RubyBasicObject) attributeType(context, attribute);
199
199
  // The type or its delegate is an OID::Array
200
- if (arrayClass.isInstance(attributeType) ||
200
+ if (attributeType != null && (arrayClass.isInstance(attributeType) ||
201
201
  (attributeType.hasInstanceVariable("@delegate_dc_obj") &&
202
- arrayClass.isInstance(attributeType.getInstanceVariable("@delegate_dc_obj")))) {
202
+ arrayClass.isInstance(attributeType.getInstanceVariable("@delegate_dc_obj"))))) {
203
203
  return "array";
204
204
  }
205
205
 
@@ -436,7 +436,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
436
436
  break;
437
437
 
438
438
  case "interval":
439
- statement.setObject(index, new PGInterval(value.toString()));
439
+ statement.setObject(index, stringToPGInterval(value.toString()));
440
440
  break;
441
441
 
442
442
  case "json":
@@ -493,6 +493,74 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
493
493
  }
494
494
  }
495
495
 
496
+ private int lookAhead(String value, int position, String find) {
497
+ char [] tokens = find.toCharArray();
498
+ int found = -1;
499
+
500
+ for ( int i = 0; i < tokens.length; i++ ) {
501
+ found = value.indexOf(tokens[i], position);
502
+ if ( found > 0 ) {
503
+ return found;
504
+ }
505
+ }
506
+ return found;
507
+ }
508
+
509
+ private Object stringToPGInterval(String value) throws SQLException {
510
+ if (!value.startsWith("P")) return new PGInterval(value);
511
+
512
+ PGInterval interval = new PGInterval();
513
+
514
+ /* this is copied from pgjdbc with fixes for Rails */
515
+ int number = 0;
516
+ String dateValue;
517
+ String timeValue = null;
518
+
519
+ int hasTime = value.indexOf('T');
520
+ if ( hasTime > 0 ) {
521
+ /* skip over the P */
522
+ dateValue = value.substring(1,hasTime);
523
+ timeValue = value.substring(hasTime + 1);
524
+ } else {
525
+ /* skip over the P */
526
+ dateValue = value.substring(1);
527
+ }
528
+
529
+ for ( int i = 0; i < dateValue.length(); i++ ) {
530
+ int lookAhead = lookAhead(dateValue, i, "YMD");
531
+ if (lookAhead > 0) {
532
+ char type = dateValue.charAt(lookAhead);
533
+ number = Integer.parseInt(dateValue.substring(i, lookAhead));
534
+ if (type == 'Y') {
535
+ interval.setYears(number);
536
+ } else if (type == 'M') {
537
+ interval.setMonths(number);
538
+ } else if (type == 'D') {
539
+ interval.setDays(number);
540
+ }
541
+ i = lookAhead;
542
+ }
543
+ }
544
+ if ( timeValue != null ) {
545
+ for (int i = 0; i < timeValue.length(); i++) {
546
+ int lookAhead = lookAhead(timeValue, i, "HMS");
547
+ if (lookAhead > 0) {
548
+ char type = timeValue.charAt(lookAhead);
549
+ String part = timeValue.substring(i, lookAhead);
550
+ if (timeValue.charAt(lookAhead) == 'H') {
551
+ interval.setHours(Integer.parseInt(part));
552
+ } else if (timeValue.charAt(lookAhead) == 'M') {
553
+ interval.setMinutes(Integer.parseInt(part));
554
+ } else if (timeValue.charAt(lookAhead) == 'S') {
555
+ interval.setSeconds(Double.parseDouble(part));
556
+ }
557
+ i = lookAhead;
558
+ }
559
+ }
560
+ }
561
+ return interval;
562
+ }
563
+
496
564
  protected IRubyObject jdbcToRuby(ThreadContext context, Ruby runtime, int column, int type, ResultSet resultSet) throws SQLException {
497
565
  return typeMap != null ?
498
566
  convertWithTypeMap(context, runtime, column, type, resultSet) :
@@ -874,34 +942,25 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
874
942
  private static String formatInterval(final Object object) {
875
943
  final PGInterval interval = (PGInterval) object;
876
944
  final StringBuilder str = new StringBuilder(32);
945
+ str.append("P");
877
946
 
878
947
  final int years = interval.getYears();
879
- if (years != 0) str.append(years).append(" years ");
948
+ if (years != 0) str.append(years).append("Y");
880
949
 
881
950
  final int months = interval.getMonths();
882
- if (months != 0) str.append(months).append(" months ");
951
+ if (months != 0) str.append(months).append("M");
883
952
 
884
953
  final int days = interval.getDays();
885
- if (days != 0) str.append(days).append(" days ");
954
+ if (days != 0) str.append(days).append("D");
886
955
 
887
956
  final int hours = interval.getHours();
888
957
  final int mins = interval.getMinutes();
889
- final int secs = (int) interval.getSeconds();
890
- if (hours != 0 || mins != 0 || secs != 0) { // xx:yy:zz if not all 00
891
- if (hours < 10) str.append('0');
892
-
893
- str.append(hours).append(':');
894
-
895
- if (mins < 10) str.append('0');
896
-
897
- str.append(mins).append(':');
898
-
899
- if (secs < 10) str.append('0');
900
-
901
- str.append(secs);
902
-
903
- } else if (str.length() > 1) {
904
- str.deleteCharAt(str.length() - 1); // " " at the end
958
+ final double secs = interval.getSeconds();
959
+ if (hours != 0 || mins != 0 || secs != 0) {
960
+ str.append("T");
961
+ if (hours != 0) str.append(hours).append("H");
962
+ if (mins != 0) str.append(mins).append("M");
963
+ if (secs != 0) str.append(secs).append("S");
905
964
  }
906
965
 
907
966
  return str.toString();
@@ -370,10 +370,9 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
370
370
  }
371
371
 
372
372
  @Override
373
- public IRubyObject begin(ThreadContext context, IRubyObject level) {
374
- throw context.runtime.newRaiseException(getTransactionIsolationError(context.runtime),
375
- "SQLite3 does not support isolation levels"
376
- );
373
+ protected boolean resetSavepoints(final ThreadContext context, final Connection connection) throws SQLException {
374
+ connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
375
+ return super.resetSavepoints(context, connection);
377
376
  }
378
377
 
379
378
  @Override
metadata CHANGED
@@ -1,21 +1,21 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-jdbc-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: '60.4'
4
+ version: '61.2'
5
5
  platform: java
6
6
  authors:
7
7
  - Nick Sieger, Ola Bini, Karol Bucek and JRuby contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-29 00:00:00.000000000 Z
11
+ date: 2022-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 6.0.0
18
+ version: 6.1.0
19
19
  name: activerecord
20
20
  prerelease: false
21
21
  type: :runtime
@@ -23,7 +23,7 @@ dependencies:
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 6.0.0
26
+ version: 6.1.0
27
27
  description: 'AR-JDBC is a database adapter for Rails'' ActiveRecord component designed
28
28
  to be used with JRuby built upon Java''s JDBC API for database access. Provides
29
29
  (ActiveRecord) built-in adapters: MySQL, PostgreSQL, SQLite3, and SQLServer.'
@@ -35,6 +35,7 @@ executables: []
35
35
  extensions: []
36
36
  extra_rdoc_files: []
37
37
  files:
38
+ - ".github/workflows/ruby.yml"
38
39
  - ".gitignore"
39
40
  - ".travis.yml"
40
41
  - ".yardopts"
@@ -230,7 +231,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
231
  - !ruby/object:Gem::Version
231
232
  version: '0'
232
233
  requirements: []
233
- rubygems_version: 3.2.14
234
+ rubygems_version: 3.2.29
234
235
  signing_key:
235
236
  specification_version: 4
236
237
  summary: JDBC adapter for ActiveRecord, for use within JRuby on Rails.