1111import java .sql .ResultSet ;
1212import java .sql .SQLException ;
1313import java .sql .Statement ;
14- import java .sql .Timestamp ;
1514import java .sql .Types ;
1615import java .util .ArrayList ;
1716import java .util .Collection ;
@@ -880,6 +879,28 @@ public String getRangeString(Range range) {
880879 "The GenericDriver cannot perform a range during select" );
881880 }
882881
882+ /**
883+ * Whether this driver's range/paging clause requires an ORDER BY to be
884+ * well-formed. ANSI {@code OFFSET ? ROWS FETCH NEXT ? ROWS ONLY} (SQL Server
885+ * 2012+, Oracle 12c+) is only valid when preceded by an ORDER BY; LIMIT/OFFSET
886+ * (Postgres) and Derby's FETCH FIRST do not have this requirement. When true
887+ * and a paged query supplies no explicit sort, {@link #getDefaultRangeOrderBy()}
888+ * is appended before the range clause.
889+ */
890+ protected boolean rangeRequiresOrderBy () {
891+ return false ;
892+ }
893+
894+ /**
895+ * The fallback ORDER BY clause to append when a paged query supplies no
896+ * explicit sort order and {@link #rangeRequiresOrderBy()} is true. Drivers
897+ * that require ORDER BY for paging override this.
898+ */
899+ protected String getDefaultRangeOrderBy () {
900+ throw new UnsupportedOperationException (
901+ "Driver does not define a default range ORDER BY clause" );
902+ }
903+
883904 /*private void setAutoFields(Connection c, MetaClass mc,
884905 ObjectMappingDB omdb, Collection<MetaField> fields, Object o,
885906 int mode ) throws SQLException {
@@ -1504,15 +1525,6 @@ protected PreparedStatement getSelectStatementWhere(Connection c, MetaObject mc,
15041525 if (options .isDistinct ()) {
15051526 query .append ("DISTINCT " );
15061527 }
1507- if (options .getRange () != null ) {
1508- // TODO: Make this a little smarter for christ's sake!
1509- // Recommendation: Have the driver form the final SELECT call, then
1510- // it can insert the limit where needed
1511- //if (getDatabaseDriver() instanceof MSSQLDriver) {
1512- // query.append("TOP ").append(options.getRange().getEnd())
1513- // .append(' ');
1514- //}
1515- }
15161528
15171529 String fieldStr = getFieldString (omdb , fields , "A" );
15181530
@@ -1555,24 +1567,30 @@ protected PreparedStatement getSelectStatementWhere(Connection c, MetaObject mc,
15551567 query .append (" WHERE " ).append (getExpressionString (mc , omdb , where , args , "A" ));
15561568 }
15571569
1570+ Range range = options .getRange ();
1571+ boolean applyRange = supportsRangeInQuery () && range != null
1572+ && range .getStart () > 0 && range .getEnd () > 0 ;
1573+
1574+ if (applyRange && range .getStart () > range .getEnd ()) {
1575+ throw new IllegalArgumentException ("The range end (" + range .getEnd () + ") cannot be greater than the start value (" + range .getStart () + ")" );
1576+ }
1577+
15581578 if (order != null ) {
15591579 query .append (" ORDER BY " ).append (getOrderString (mc , omdb , order ));
1580+ } else if (applyRange && rangeRequiresOrderBy ()) {
1581+ // ANSI OFFSET/FETCH (SQL Server 2012+, Oracle 12c+) is only valid
1582+ // when preceded by an ORDER BY. Supply a deterministic fallback when
1583+ // the caller did not specify a sort order.
1584+ query .append (" " ).append (getDefaultRangeOrderBy ());
15601585 }
15611586
1562- if (options .withLock ()) {
1563- query .append (" " ).append (getLockString ());
1587+ // Add on the range (must follow ORDER BY for OFFSET/FETCH dialects)
1588+ if (applyRange ) {
1589+ query .append (" " ).append (getRangeString (range ));
15641590 }
15651591
1566- // Add on the range
1567- Range range = options .getRange ();
1568- if (supportsRangeInQuery () && options .getRange () != null
1569- && range .getStart () > 0 && range .getEnd () > 0 ) {
1570-
1571- if (range .getStart () > range .getEnd ()) {
1572- throw new IllegalArgumentException ("The range end (" + range .getEnd () + ") cannot be greater than the start value (" + range .getStart () + ")" );
1573- }
1574-
1575- query .append (" " ).append (getRangeString (range ));
1592+ if (options .withLock ()) {
1593+ query .append (" " ).append (getLockString ());
15761594 }
15771595
15781596 PreparedStatement s = c .prepareStatement (query .toString ());
@@ -1746,6 +1764,13 @@ protected PreparedStatement getInsertStatement(Connection c, MetaObject mc,
17461764 if (colDef .getAutoType () == ColumnDef .AUTO_ID ) {
17471765 f .setString (o , getNextAutoId (c , colDef ));
17481766 }
1767+ else if (colDef .getAutoType () == ColumnDef .AUTO_UUID ) {
1768+ // App-side UUID PK: OMDB mints the value before INSERT (no DB
1769+ // identity / default). DB-portable as a string column.
1770+ if (f .getString (o ) == null ) {
1771+ f .setString (o , java .util .UUID .randomUUID ().toString ());
1772+ }
1773+ }
17491774 else if (colDef .isAutoIncrementor ()) {
17501775 continue ;
17511776 }
@@ -1950,37 +1975,23 @@ protected void parseObject(ResultSet rs, Collection<MetaField> fields,
19501975 }
19511976
19521977 /**
1953- * Enhanced field parsing with modern Java patterns and proper null handling
1978+ * Reads a single column from the ResultSet into the target object via the
1979+ * {@link com.metaobjects.manager.db.codec.JdbcCodecs} registry — the same
1980+ * single source of truth used by {@link #setStatementValue} on the write
1981+ * side (FR-003 Plan 4, Debt 1 — ADR-0002). This replaces the former
1982+ * {@code instanceof} ladder, which had drifted from the write-side codec
1983+ * (e.g. TimeField).
1984+ *
1985+ * <p>The jsonb pre-check mirrors {@code setStatementValue}: jsonb
1986+ * serialization depends on driver-local state ({@link #isJsonbField} /
1987+ * {@link #deserializeJsonb}), not on field subtype alone, and is gated on
1988+ * {@code codec == defaultCodec()} so explicit per-subtype codecs always win.
19541989 */
19551990 protected void parseField (Object o , MetaField f , ResultSet rs , int j ) throws SQLException {
1956- if (f instanceof com .metaobjects .field .BooleanField ) {
1957- boolean bv = rs .getBoolean (j );
1958- f .setBoolean (o , rs .wasNull () ? null : bv );
1959- } else if (f instanceof com .metaobjects .field .DecimalField ) {
1960- java .math .BigDecimal dv = rs .getBigDecimal (j );
1961- f .setObject (o , rs .wasNull () ? null : dv );
1962- } else if (f instanceof com .metaobjects .field .IntegerField ) {
1963- int iv = rs .getInt (j );
1964- f .setInt (o , rs .wasNull () ? null : iv );
1965- } else if (f instanceof com .metaobjects .field .DateField ) {
1966- Timestamp tv = rs .getTimestamp (j );
1967- f .setDate (o , rs .wasNull () ? null : new Date (tv .getTime ()));
1968- } else if (f instanceof com .metaobjects .field .TimeField ) {
1969- java .sql .Time tv = rs .getTime (j );
1970- f .setObject (o , rs .wasNull () ? null : tv .toLocalTime ());
1971- } else if (f instanceof com .metaobjects .field .LongField ) {
1972- long lv = rs .getLong (j );
1973- f .setLong (o , rs .wasNull () ? null : lv );
1974- } else if (f instanceof com .metaobjects .field .FloatField ) {
1975- float fv = rs .getFloat (j );
1976- f .setFloat (o , rs .wasNull () ? null : fv );
1977- } else if (f instanceof com .metaobjects .field .DoubleField ) {
1978- double dv = rs .getDouble (j );
1979- f .setDouble (o , rs .wasNull () ? null : dv );
1980- } else if (f instanceof com .metaobjects .field .StringField ) {
1981- f .setString (o , rs .getString (j ));
1982- } else if (isJsonbField (f )) {
1983- // jsonb: read JSON text from column, deserialize via deserializeJsonb().
1991+ com .metaobjects .manager .db .codec .JdbcFieldCodec codec =
1992+ com .metaobjects .manager .db .codec .JdbcCodecs .forField (f );
1993+ if (codec == com .metaobjects .manager .db .codec .JdbcCodecs .defaultCodec () && isJsonbField (f )) {
1994+ // jsonb: read JSON text from the column, deserialize via deserializeJsonb().
19841995 // Step 1 consults ObjectClassRegistry for a bound class (registry wins for
19851996 // explicit POJO bindings); step 2 falls back to MetaObjectDeserializer which
19861997 // returns the MetaObject's declared class (e.g. ValueObject) when no binding
@@ -1991,12 +2002,9 @@ protected void parseField(Object o, MetaField f, ResultSet rs, int j) throws SQL
19912002 } else {
19922003 f .setObject (o , deserializeJsonb (f , json ));
19932004 }
1994- } else if (f instanceof com .metaobjects .field .ObjectField ) {
1995- f .setObject (o , rs .getObject (j ));
1996- } else {
1997- log .warn ("Unknown field type {} for field {}, defaulting to Object" , f .getClass ().getSimpleName (), f .getName ());
1998- f .setObject (o , rs .getObject (j ));
2005+ return ;
19992006 }
2007+ codec .readInto (o , f , rs , j );
20002008 }
20012009
20022010 // /////////////////////////////////////////////////////
0 commit comments