View Javadoc
1   /*
2    * Prometheus: Application Framework
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.prometheus.database;
18  
19  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20  import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
21  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
22  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
23  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRate;
24  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
25  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
26  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
27  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
28  import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
29  import io.github.tonywasher.joceanus.prometheus.database.PrometheusTableDefinition.PrometheusSortOrder;
30  import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;
31  import io.github.tonywasher.joceanus.prometheus.preference.PrometheusColumnType;
32  
33  import java.math.BigDecimal;
34  import java.sql.Date;
35  import java.sql.PreparedStatement;
36  import java.sql.ResultSet;
37  import java.sql.SQLException;
38  import java.sql.Types;
39  import java.util.Iterator;
40  import java.util.List;
41  import java.util.ListIterator;
42  
43  /**
44   * Column definition classes handling data-type specifics.
45   *
46   * @author Tony Washer
47   */
48  public abstract class PrometheusColumnDefinition {
49      /**
50       * Open bracket.
51       */
52      private static final String STR_OPNBRK = "(";
53  
54      /**
55       * Close bracket.
56       */
57      private static final String STR_CLSBRK = ")";
58  
59      /**
60       * Comma.
61       */
62      private static final String STR_COMMA = ",";
63  
64      /**
65       * Decimal length.
66       */
67      private static final String STR_NUMLEN = "18";
68  
69      /**
70       * Standard Decimal length.
71       */
72      private static final String STR_STDDECLEN = "4";
73  
74      /**
75       * Decimal length.
76       */
77      private static final String STR_XTRADECLEN = "6";
78  
79      /**
80       * Table Definition.
81       */
82      private final PrometheusTableDefinition theTable;
83  
84      /**
85       * Column Identity.
86       */
87      private final MetisDataFieldId theIdentity;
88  
89      /**
90       * Is the column null-able.
91       */
92      private boolean isNullable;
93  
94      /**
95       * Is the value set.
96       */
97      private boolean isValueSet;
98  
99      /**
100      * The value of the column.
101      */
102     private Object theValue;
103 
104     /**
105      * The sort order of the column.
106      */
107     private PrometheusSortOrder theOrder;
108 
109     /**
110      * Constructor.
111      *
112      * @param pTable the table to which the column belongs
113      * @param pId    the column id
114      */
115     protected PrometheusColumnDefinition(final PrometheusTableDefinition pTable,
116                                          final MetisDataFieldId pId) {
117         /* Record the identity and table */
118         theIdentity = pId;
119         theTable = pTable;
120 
121         /* Add to the map */
122         theTable.getMap().put(theIdentity, this);
123     }
124 
125     /**
126      * Obtain the column name.
127      *
128      * @return the name
129      */
130     protected String getColumnName() {
131         return theIdentity.getId();
132     }
133 
134     /**
135      * Obtain the column id.
136      *
137      * @return the id
138      */
139     protected MetisDataFieldId getColumnId() {
140         return theIdentity;
141     }
142 
143     /**
144      * Obtain the sort order.
145      *
146      * @return the sort order
147      */
148     protected PrometheusSortOrder getSortOrder() {
149         return theOrder;
150     }
151 
152     /**
153      * Obtain the value.
154      *
155      * @return the value
156      */
157     protected Object getObject() {
158         return theValue;
159     }
160 
161     /**
162      * Obtain the table.
163      *
164      * @return the table
165      */
166     protected PrometheusTableDefinition getTable() {
167         return theTable;
168     }
169 
170     /**
171      * Obtain the value.
172      *
173      * @return the value
174      */
175     protected PrometheusJDBCDriver getDriver() {
176         return theTable.getDriver();
177     }
178 
179     /**
180      * Clear value.
181      */
182     protected void clearValue() {
183         theValue = null;
184         isValueSet = false;
185     }
186 
187     /**
188      * Set value.
189      *
190      * @param pValue the value
191      */
192     protected void setObject(final Object pValue) {
193         theValue = pValue;
194         isValueSet = true;
195     }
196 
197     /**
198      * Is the value set.
199      *
200      * @return true/false
201      */
202     protected boolean isValueSet() {
203         return isValueSet;
204     }
205 
206     /**
207      * Build the creation string for this column.
208      *
209      * @param pBuilder the String builder
210      */
211     protected void buildCreateString(final StringBuilder pBuilder) {
212         /* Add the name of the column */
213         theTable.addQuoteIfAllowed(pBuilder);
214         pBuilder.append(getColumnName());
215         theTable.addQuoteIfAllowed(pBuilder);
216         pBuilder.append(' ');
217 
218         /* Add the type of the column */
219         buildColumnType(pBuilder);
220 
221         /* Add null-able indication */
222         if (!isNullable) {
223             pBuilder.append(" not");
224         }
225         pBuilder.append(" null");
226 
227         /* build the key reference */
228         buildKeyReference(pBuilder);
229     }
230 
231     /**
232      * Set null-able column.
233      */
234     protected void setNullable() {
235         isNullable = true;
236     }
237 
238     /**
239      * Note that we have a sort on reference.
240      */
241     protected void setSortOnReference() {
242         theTable.setSortOnReference();
243     }
244 
245     /**
246      * Set sortOrder.
247      *
248      * @param pOrder the Sort direction
249      */
250     public void setSortOrder(final PrometheusSortOrder pOrder) {
251         theOrder = pOrder;
252         theTable.getSortList().add(this);
253     }
254 
255     /**
256      * Build the column type for this column.
257      *
258      * @param pBuilder the String builder
259      */
260     protected abstract void buildColumnType(StringBuilder pBuilder);
261 
262     /**
263      * Load the value for this column.
264      *
265      * @param pResults the results
266      * @param pIndex   the index of the result column
267      * @throws SQLException on error
268      */
269     protected abstract void loadValue(ResultSet pResults,
270                                       int pIndex) throws SQLException;
271 
272     /**
273      * Store the value for this column.
274      *
275      * @param pStatement the prepared statement
276      * @param pIndex     the index of the statement
277      * @throws SQLException on error
278      */
279     protected abstract void storeValue(PreparedStatement pStatement,
280                                        int pIndex) throws SQLException;
281 
282     /**
283      * Define the key reference.
284      *
285      * @param pBuilder the String builder
286      */
287     protected void buildKeyReference(final StringBuilder pBuilder) {
288     }
289 
290     /**
291      * Locate reference.
292      *
293      * @param pTables the list of defined tables
294      */
295     protected void locateReference(final List<PrometheusTableDataItem<?>> pTables) {
296     }
297 
298     /**
299      * The integerColumn Class.
300      */
301     protected static class PrometheusIntegerColumn
302             extends PrometheusColumnDefinition {
303         /**
304          * Constructor.
305          *
306          * @param pTable the table to which the column belongs
307          * @param pId    the column id
308          */
309         protected PrometheusIntegerColumn(final PrometheusTableDefinition pTable,
310                                           final MetisDataFieldId pId) {
311             /* Record the column type and name */
312             super(pTable, pId);
313         }
314 
315         @Override
316         protected void buildColumnType(final StringBuilder pBuilder) {
317             /* Add the column type */
318             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.INTEGER));
319         }
320 
321         /**
322          * Set the value.
323          *
324          * @param pValue the value
325          */
326         protected void setValue(final Integer pValue) {
327             super.setObject(pValue);
328         }
329 
330         /**
331          * Get the value.
332          *
333          * @return the value
334          */
335         protected Integer getValue() {
336             return (Integer) super.getObject();
337         }
338 
339         @Override
340         protected void loadValue(final ResultSet pResults,
341                                  final int pIndex) throws SQLException {
342             final int myValue = pResults.getInt(pIndex);
343             if (pResults.wasNull()) {
344                 setValue(null);
345             } else {
346                 setValue(myValue);
347             }
348         }
349 
350         @Override
351         protected void storeValue(final PreparedStatement pStatement,
352                                   final int pIndex) throws SQLException {
353             final Integer myValue = getValue();
354             if (myValue == null) {
355                 pStatement.setNull(pIndex, Types.INTEGER);
356             } else {
357                 pStatement.setInt(pIndex, myValue);
358             }
359         }
360     }
361 
362     /**
363      * The idColumn Class.
364      */
365     protected static final class PrometheusIdColumn
366             extends PrometheusIntegerColumn {
367         /**
368          * Constructor.
369          *
370          * @param pTable the table to which the column belongs
371          */
372         PrometheusIdColumn(final PrometheusTableDefinition pTable) {
373             /* Record the column type */
374             super(pTable, MetisDataResource.DATA_ID);
375         }
376 
377         @Override
378         protected void buildKeyReference(final StringBuilder pBuilder) {
379             /* Add the column type */
380             pBuilder.append(" primary key");
381         }
382     }
383 
384     /**
385      * The referenceColumn Class.
386      */
387     protected static final class PrometheusReferenceColumn
388             extends PrometheusIntegerColumn {
389         /**
390          * The name of the referenced table.
391          */
392         private final String theReference;
393 
394         /**
395          * The definition of the referenced table.
396          */
397         private PrometheusTableDefinition theDefinition;
398 
399         /**
400          * Constructor.
401          *
402          * @param pTable    the table to which the column belongs
403          * @param pId       the column id
404          * @param pRefTable the name of the referenced table
405          */
406         PrometheusReferenceColumn(final PrometheusTableDefinition pTable,
407                                   final MetisDataFieldId pId,
408                                   final String pRefTable) {
409             /* Record the column type */
410             super(pTable, pId);
411             theReference = pRefTable;
412         }
413 
414         @Override
415         public void setSortOrder(final PrometheusSortOrder pOrder) {
416             super.setSortOrder(pOrder);
417             setSortOnReference();
418         }
419 
420         @Override
421         protected void buildKeyReference(final StringBuilder pBuilder) {
422             /* Add the reference */
423             pBuilder.append(" REFERENCES ");
424             getTable().addQuoteIfAllowed(pBuilder);
425             pBuilder.append(theReference);
426             getTable().addQuoteIfAllowed(pBuilder);
427             pBuilder.append(STR_OPNBRK);
428             getTable().addQuoteIfAllowed(pBuilder);
429             pBuilder.append(MetisDataResource.DATA_ID.getValue());
430             getTable().addQuoteIfAllowed(pBuilder);
431             pBuilder.append(STR_CLSBRK);
432         }
433 
434         @Override
435         protected void locateReference(final List<PrometheusTableDataItem<?>> pTables) {
436             /* Access the Iterator */
437             final ListIterator<PrometheusTableDataItem<?>> myIterator;
438             myIterator = pTables.listIterator();
439 
440             /* Loop through the Tables */
441             while (myIterator.hasNext()) {
442                 /* Access Table */
443                 final PrometheusTableDataItem<?> myTable = myIterator.next();
444 
445                 /* If this is the referenced table */
446                 if (theReference.compareTo(myTable.getTableName()) == 0) {
447                     /* Store the reference and break the loop */
448                     theDefinition = myTable.getDefinition();
449                     break;
450                 }
451             }
452         }
453 
454         /**
455          * build Join String.
456          *
457          * @param pBuilder the String Builder
458          * @param pChar    the character for this table
459          * @param pOffset  the join offset
460          */
461         void buildJoinString(final StringBuilder pBuilder,
462                              final char pChar,
463                              final Integer pOffset) {
464             Integer myOffset = pOffset;
465 
466             /* Calculate join character */
467             final char myChar = (char) ('a' + myOffset);
468 
469             /* Build Initial part of string */
470             pBuilder.append(" join ");
471             getTable().addQuoteIfAllowed(pBuilder);
472             pBuilder.append(theReference);
473             getTable().addQuoteIfAllowed(pBuilder);
474             pBuilder.append(" ");
475             pBuilder.append(myChar);
476 
477             /* Build the join */
478             pBuilder.append(" on ");
479             pBuilder.append(pChar);
480             pBuilder.append(".");
481             getTable().addQuoteIfAllowed(pBuilder);
482             pBuilder.append(getColumnName());
483             getTable().addQuoteIfAllowed(pBuilder);
484             pBuilder.append(" = ");
485             pBuilder.append(myChar);
486             pBuilder.append(".");
487             getTable().addQuoteIfAllowed(pBuilder);
488             pBuilder.append(MetisDataResource.DATA_ID.getValue());
489             getTable().addQuoteIfAllowed(pBuilder);
490 
491             /* Increment offset */
492             myOffset++;
493 
494             /* Add the join string for the underlying table */
495             pBuilder.append(theDefinition.getJoinString(myChar, myOffset));
496         }
497 
498         /**
499          * build Order String.
500          *
501          * @param pBuilder the String Builder
502          * @param pOffset  the join offset
503          */
504         void buildOrderString(final StringBuilder pBuilder,
505                               final Integer pOffset) {
506             final Iterator<PrometheusColumnDefinition> myIterator;
507             PrometheusColumnDefinition myDef;
508             boolean myFirst = true;
509             Integer myOffset = pOffset;
510 
511             /* Calculate join character */
512             final char myChar = (char) ('a' + myOffset);
513 
514             /* Create the iterator */
515             myIterator = theDefinition.getSortList().iterator();
516 
517             /* Loop through the columns */
518             while (myIterator.hasNext()) {
519                 /* Access next column */
520                 myDef = myIterator.next();
521 
522                 /* Handle subsequent columns */
523                 if (!myFirst) {
524                     pBuilder.append(", ");
525                 }
526 
527                 /* If this is a reference column */
528                 if (myDef instanceof PrometheusReferenceColumn myColumn) {
529                     /* Increment offset */
530                     myOffset++;
531 
532                     /* Determine new char */
533                     final char myNewChar = (char) ('a' + myOffset);
534 
535                     /* Add the order string for the underlying table. */
536                     /* Note that forced to implement in one line to avoid Sonar false positive. */
537                     pBuilder.append(myColumn.theDefinition.getOrderString(myNewChar, myOffset));
538 
539                     /* else standard column */
540                 } else {
541                     /* Build the column name */
542                     pBuilder.append(myChar);
543                     pBuilder.append(".");
544                     getTable().addQuoteIfAllowed(pBuilder);
545                     pBuilder.append(myDef.getColumnName());
546                     getTable().addQuoteIfAllowed(pBuilder);
547                     if (myDef.getSortOrder() == PrometheusSortOrder.DESCENDING) {
548                         pBuilder.append(" DESC");
549                     }
550                 }
551 
552                 /* Note we have a column */
553                 myFirst = false;
554             }
555         }
556     }
557 
558     /**
559      * The shortColumn Class.
560      */
561     protected static final class PrometheusShortColumn
562             extends PrometheusColumnDefinition {
563         /**
564          * Constructor.
565          *
566          * @param pTable the table to which the column belongs
567          * @param pId    the column id
568          */
569         PrometheusShortColumn(final PrometheusTableDefinition pTable,
570                               final MetisDataFieldId pId) {
571             /* Record the column type */
572             super(pTable, pId);
573         }
574 
575         @Override
576         protected void buildColumnType(final StringBuilder pBuilder) {
577             /* Add the column type */
578             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.SHORT));
579         }
580 
581         /**
582          * Set the value.
583          *
584          * @param pValue the value
585          */
586         void setValue(final Short pValue) {
587             super.setObject(pValue);
588         }
589 
590         /**
591          * Get the value.
592          *
593          * @return the value
594          */
595         Short getValue() {
596             return (Short) super.getObject();
597         }
598 
599         @Override
600         protected void loadValue(final ResultSet pResults,
601                                  final int pIndex) throws SQLException {
602             final short myValue = pResults.getShort(pIndex);
603             if (pResults.wasNull()) {
604                 setValue(null);
605             } else {
606                 setValue(myValue);
607             }
608         }
609 
610         @Override
611         protected void storeValue(final PreparedStatement pStatement,
612                                   final int pIndex) throws SQLException {
613             final Short myValue = getValue();
614             if (myValue == null) {
615                 pStatement.setNull(pIndex, Types.SMALLINT);
616             } else {
617                 pStatement.setShort(pIndex, myValue);
618             }
619         }
620     }
621 
622     /**
623      * The longColumn Class.
624      */
625     protected static final class PrometheusLongColumn
626             extends PrometheusColumnDefinition {
627         /**
628          * Constructor.
629          *
630          * @param pTable the table to which the column belongs
631          * @param pId    the column id
632          */
633         PrometheusLongColumn(final PrometheusTableDefinition pTable,
634                              final MetisDataFieldId pId) {
635             /* Record the column type */
636             super(pTable, pId);
637         }
638 
639         @Override
640         protected void buildColumnType(final StringBuilder pBuilder) {
641             /* Add the column type */
642             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.LONG));
643         }
644 
645         /**
646          * Set the value.
647          *
648          * @param pValue the value
649          */
650         void setValue(final Long pValue) {
651             super.setObject(pValue);
652         }
653 
654         /**
655          * Get the value.
656          *
657          * @return the value
658          */
659         Long getValue() {
660             return (Long) super.getObject();
661         }
662 
663         @Override
664         protected void loadValue(final ResultSet pResults,
665                                  final int pIndex) throws SQLException {
666             final long myValue = pResults.getLong(pIndex);
667             if (pResults.wasNull()) {
668                 setValue(null);
669             } else {
670                 setValue(myValue);
671             }
672         }
673 
674         @Override
675         protected void storeValue(final PreparedStatement pStatement,
676                                   final int pIndex) throws SQLException {
677             final Long myValue = getValue();
678             if (myValue == null) {
679                 pStatement.setNull(pIndex, Types.BIGINT);
680             } else {
681                 pStatement.setLong(pIndex, myValue);
682             }
683         }
684     }
685 
686     /**
687      * The dateColumn Class.
688      */
689     protected static final class PrometheusDateColumn
690             extends PrometheusColumnDefinition {
691         /**
692          * Constructor.
693          *
694          * @param pTable the table to which the column belongs
695          * @param pId    the column id
696          */
697         PrometheusDateColumn(final PrometheusTableDefinition pTable,
698                              final MetisDataFieldId pId) {
699             /* Record the column type */
700             super(pTable, pId);
701         }
702 
703         @Override
704         protected void buildColumnType(final StringBuilder pBuilder) {
705             /* Add the column type */
706             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.DATE));
707         }
708 
709         /**
710          * Set the value.
711          *
712          * @param pValue the value
713          */
714         void setValue(final OceanusDate pValue) {
715             super.setObject(pValue);
716         }
717 
718         /**
719          * Get the value.
720          *
721          * @return the value
722          */
723         OceanusDate getValue() {
724             return (OceanusDate) super.getObject();
725         }
726 
727         @Override
728         protected void loadValue(final ResultSet pResults,
729                                  final int pIndex) throws SQLException {
730             final Date myValue = pResults.getDate(pIndex);
731             setValue((myValue == null)
732                     ? null
733                     : new OceanusDate(myValue));
734         }
735 
736         @Override
737         protected void storeValue(final PreparedStatement pStatement,
738                                   final int pIndex) throws SQLException {
739             final OceanusDate myValue = getValue();
740 
741             /* Build the date as a SQL date */
742             if (myValue == null) {
743                 pStatement.setNull(pIndex, Types.DATE);
744             } else {
745                 final long myDateValue = myValue.toDate().getTime();
746                 final Date myDate = new Date(myDateValue);
747                 pStatement.setDate(pIndex, myDate);
748             }
749         }
750     }
751 
752     /**
753      * The booleanColumn Class.
754      */
755     protected static final class PrometheusBooleanColumn
756             extends PrometheusColumnDefinition {
757         /**
758          * Constructor.
759          *
760          * @param pTable the table to which the column belongs
761          * @param pId    the column id
762          */
763         PrometheusBooleanColumn(final PrometheusTableDefinition pTable,
764                                 final MetisDataFieldId pId) {
765             /* Record the column type */
766             super(pTable, pId);
767         }
768 
769         @Override
770         protected void buildColumnType(final StringBuilder pBuilder) {
771             /* Add the column type */
772             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.BOOLEAN));
773         }
774 
775         /**
776          * Set the value.
777          *
778          * @param pValue the value
779          */
780         void setValue(final Boolean pValue) {
781             super.setObject(pValue);
782         }
783 
784         /**
785          * Get the value.
786          *
787          * @return the value
788          */
789         Boolean getValue() {
790             return (Boolean) super.getObject();
791         }
792 
793         @Override
794         protected void loadValue(final ResultSet pResults,
795                                  final int pIndex) throws SQLException {
796             final boolean myValue = pResults.getBoolean(pIndex);
797             if (pResults.wasNull()) {
798                 setValue(null);
799             } else {
800                 setValue(myValue);
801             }
802         }
803 
804         @Override
805         protected void storeValue(final PreparedStatement pStatement,
806                                   final int pIndex) throws SQLException {
807             final Boolean myValue = getValue();
808             if (myValue == null) {
809                 pStatement.setNull(pIndex, Types.BIT);
810             } else {
811                 pStatement.setBoolean(pIndex, myValue);
812             }
813         }
814     }
815 
816     /**
817      * The stringColumn Class.
818      */
819     protected static class PrometheusStringColumn
820             extends PrometheusColumnDefinition {
821         /**
822          * The length of the column.
823          */
824         private final int theLength;
825 
826         /**
827          * Constructor.
828          *
829          * @param pTable  the table to which the column belongs
830          * @param pId     the column id
831          * @param pLength the length
832          */
833         protected PrometheusStringColumn(final PrometheusTableDefinition pTable,
834                                          final MetisDataFieldId pId,
835                                          final int pLength) {
836             /* Record the column type */
837             super(pTable, pId);
838             theLength = pLength;
839         }
840 
841         @Override
842         protected void buildColumnType(final StringBuilder pBuilder) {
843             /* Add the column type */
844             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.STRING));
845             pBuilder.append(STR_OPNBRK);
846             pBuilder.append(theLength);
847             pBuilder.append(STR_CLSBRK);
848         }
849 
850         /**
851          * Set the value.
852          *
853          * @param pValue the value
854          */
855         protected void setValue(final String pValue) {
856             super.setObject(pValue);
857         }
858 
859         /**
860          * Get the value.
861          *
862          * @return the value
863          */
864         protected String getValue() {
865             return (String) super.getObject();
866         }
867 
868         @Override
869         protected void loadValue(final ResultSet pResults,
870                                  final int pIndex) throws SQLException {
871             final String myValue = pResults.getString(pIndex);
872             setValue(myValue);
873         }
874 
875         @Override
876         protected void storeValue(final PreparedStatement pStatement,
877                                   final int pIndex) throws SQLException {
878             final String myValue = getValue();
879             if (myValue == null) {
880                 pStatement.setNull(pIndex, Types.VARCHAR);
881             } else {
882                 pStatement.setString(pIndex, myValue);
883             }
884         }
885     }
886 
887     /**
888      * The moneyColumn Class.
889      */
890     protected static final class PrometheusMoneyColumn
891             extends PrometheusStringColumn {
892         /**
893          * Constructor.
894          *
895          * @param pTable the table to which the column belongs
896          * @param pId    the column id
897          */
898         PrometheusMoneyColumn(final PrometheusTableDefinition pTable,
899                               final MetisDataFieldId pId) {
900             /* Record the column type */
901             super(pTable, pId, 0);
902         }
903 
904         @Override
905         protected void buildColumnType(final StringBuilder pBuilder) {
906             /* Add the column type */
907             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.MONEY));
908         }
909 
910         /**
911          * Set the value.
912          *
913          * @param pValue the value
914          */
915         void setValue(final OceanusMoney pValue) {
916             String myString = null;
917             if (pValue != null) {
918                 myString = pValue.toString();
919             }
920             super.setObject(myString);
921         }
922 
923         @Override
924         protected void storeValue(final PreparedStatement pStatement,
925                                   final int pIndex) throws SQLException {
926             final String myValue = getValue();
927             if (myValue != null) {
928                 final BigDecimal myDecimal = new BigDecimal(myValue);
929                 pStatement.setBigDecimal(pIndex, myDecimal);
930             } else {
931                 pStatement.setNull(pIndex, Types.DECIMAL);
932             }
933         }
934 
935         /**
936          * Obtain the value.
937          *
938          * @param pFormatter the data formatter
939          * @return the money value
940          * @throws OceanusException on error
941          */
942         public OceanusMoney getValue(final OceanusDataFormatter pFormatter) throws OceanusException {
943             try {
944                 return pFormatter.parseValue(getValue(), OceanusMoney.class);
945             } catch (IllegalArgumentException e) {
946                 throw new PrometheusDataException(getValue(), "Bad Money Value", e);
947             }
948         }
949     }
950 
951     /**
952      * The rateColumn Class.
953      */
954     protected static final class PrometheusRateColumn
955             extends PrometheusStringColumn {
956         /**
957          * Constructor.
958          *
959          * @param pTable the table to which the column belongs
960          * @param pId    the column id
961          */
962         PrometheusRateColumn(final PrometheusTableDefinition pTable,
963                              final MetisDataFieldId pId) {
964             /* Record the column type */
965             super(pTable, pId, 0);
966         }
967 
968         @Override
969         protected void buildColumnType(final StringBuilder pBuilder) {
970             /* Add the column type */
971             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.DECIMAL));
972             pBuilder.append(STR_OPNBRK);
973             pBuilder.append(STR_NUMLEN);
974             pBuilder.append(STR_COMMA);
975             pBuilder.append(STR_STDDECLEN);
976             pBuilder.append(STR_CLSBRK);
977         }
978 
979         /**
980          * Set the value.
981          *
982          * @param pValue the value
983          */
984         void setValue(final OceanusRate pValue) {
985             String myString = null;
986             if (pValue != null) {
987                 myString = pValue.toString();
988             }
989             super.setObject(myString);
990         }
991 
992         @Override
993         protected void storeValue(final PreparedStatement pStatement,
994                                   final int pIndex) throws SQLException {
995             final String myValue = getValue();
996             if (myValue != null) {
997                 final BigDecimal myDecimal = new BigDecimal(myValue);
998                 pStatement.setBigDecimal(pIndex, myDecimal);
999             } else {
1000                 pStatement.setNull(pIndex, Types.DECIMAL);
1001             }
1002         }
1003 
1004         /**
1005          * Obtain the value.
1006          *
1007          * @param pFormatter the data formatter
1008          * @return the money value
1009          * @throws OceanusException on error
1010          */
1011         public OceanusRate getValue(final OceanusDataFormatter pFormatter) throws OceanusException {
1012             try {
1013                 return pFormatter.parseValue(getValue(), OceanusRate.class);
1014             } catch (IllegalArgumentException e) {
1015                 throw new PrometheusDataException(getValue(), "Bad Rate Value", e);
1016             }
1017         }
1018     }
1019 
1020     /**
1021      * The priceColumn Class.
1022      */
1023     protected static final class PrometheusPriceColumn
1024             extends PrometheusStringColumn {
1025         /**
1026          * Constructor.
1027          *
1028          * @param pTable the table to which the column belongs
1029          * @param pId    the column id
1030          */
1031         PrometheusPriceColumn(final PrometheusTableDefinition pTable,
1032                               final MetisDataFieldId pId) {
1033             /* Record the column type */
1034             super(pTable, pId, 0);
1035         }
1036 
1037         @Override
1038         protected void buildColumnType(final StringBuilder pBuilder) {
1039             /* Add the column type */
1040             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.DECIMAL));
1041             pBuilder.append(STR_OPNBRK);
1042             pBuilder.append(STR_NUMLEN);
1043             pBuilder.append(STR_COMMA);
1044             pBuilder.append(STR_STDDECLEN);
1045             pBuilder.append(STR_CLSBRK);
1046         }
1047 
1048         /**
1049          * Set the value.
1050          *
1051          * @param pValue the value
1052          */
1053         void setValue(final OceanusPrice pValue) {
1054             String myString = null;
1055             if (pValue != null) {
1056                 myString = pValue.toString();
1057             }
1058             super.setObject(myString);
1059         }
1060 
1061         @Override
1062         protected void storeValue(final PreparedStatement pStatement,
1063                                   final int pIndex) throws SQLException {
1064             final String myValue = getValue();
1065             if (myValue != null) {
1066                 final BigDecimal myDecimal = new BigDecimal(myValue);
1067                 pStatement.setBigDecimal(pIndex, myDecimal);
1068             } else {
1069                 pStatement.setNull(pIndex, Types.DECIMAL);
1070             }
1071         }
1072 
1073         /**
1074          * Obtain the value.
1075          *
1076          * @param pFormatter the data formatter
1077          * @return the money value
1078          * @throws OceanusException on error
1079          */
1080         public OceanusPrice getValue(final OceanusDataFormatter pFormatter) throws OceanusException {
1081             try {
1082                 return pFormatter.parseValue(getValue(), OceanusPrice.class);
1083             } catch (IllegalArgumentException e) {
1084                 throw new PrometheusDataException(getValue(), "Bad Price Value", e);
1085             }
1086         }
1087     }
1088 
1089     /**
1090      * The unitsColumn Class.
1091      */
1092     protected static final class PrometheusUnitsColumn
1093             extends PrometheusStringColumn {
1094         /**
1095          * Constructor.
1096          *
1097          * @param pTable the table to which the column belongs
1098          * @param pId    the column id
1099          */
1100         PrometheusUnitsColumn(final PrometheusTableDefinition pTable,
1101                               final MetisDataFieldId pId) {
1102             /* Record the column type */
1103             super(pTable, pId, 0);
1104         }
1105 
1106         @Override
1107         protected void buildColumnType(final StringBuilder pBuilder) {
1108             /* Add the column type */
1109             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.DECIMAL));
1110             pBuilder.append(STR_OPNBRK);
1111             pBuilder.append(STR_NUMLEN);
1112             pBuilder.append(STR_COMMA);
1113             pBuilder.append(STR_STDDECLEN);
1114             pBuilder.append(STR_CLSBRK);
1115         }
1116 
1117         /**
1118          * Set the value.
1119          *
1120          * @param pValue the value
1121          */
1122         void setValue(final OceanusUnits pValue) {
1123             String myString = null;
1124             if (pValue != null) {
1125                 myString = pValue.toString();
1126             }
1127             super.setObject(myString);
1128         }
1129 
1130         @Override
1131         protected void storeValue(final PreparedStatement pStatement,
1132                                   final int pIndex) throws SQLException {
1133             final String myValue = getValue();
1134             if (myValue != null) {
1135                 final BigDecimal myDecimal = new BigDecimal(myValue);
1136                 pStatement.setBigDecimal(pIndex, myDecimal);
1137             } else {
1138                 pStatement.setNull(pIndex, Types.DECIMAL);
1139             }
1140         }
1141 
1142         /**
1143          * Obtain the value.
1144          *
1145          * @param pFormatter the data formatter
1146          * @return the money value
1147          * @throws OceanusException on error
1148          */
1149         public OceanusUnits getValue(final OceanusDataFormatter pFormatter) throws OceanusException {
1150             try {
1151                 return pFormatter.parseValue(getValue(), OceanusUnits.class);
1152             } catch (IllegalArgumentException e) {
1153                 throw new PrometheusDataException(getValue(), "Bad Units Value", e);
1154             }
1155         }
1156     }
1157 
1158     /**
1159      * The ratioColumn Class.
1160      */
1161     protected static final class PrometheusRatioColumn
1162             extends PrometheusStringColumn {
1163         /**
1164          * Constructor.
1165          *
1166          * @param pTable the table to which the column belongs
1167          * @param pId    the column id
1168          */
1169         PrometheusRatioColumn(final PrometheusTableDefinition pTable,
1170                               final MetisDataFieldId pId) {
1171             /* Record the column type */
1172             super(pTable, pId, 0);
1173         }
1174 
1175         @Override
1176         protected void buildColumnType(final StringBuilder pBuilder) {
1177             /* Add the column type */
1178             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.DECIMAL));
1179             pBuilder.append(STR_OPNBRK);
1180             pBuilder.append(STR_NUMLEN);
1181             pBuilder.append(STR_COMMA);
1182             pBuilder.append(STR_XTRADECLEN);
1183             pBuilder.append(STR_CLSBRK);
1184         }
1185 
1186         /**
1187          * Set the value.
1188          *
1189          * @param pValue the value
1190          */
1191         void setValue(final OceanusRatio pValue) {
1192             String myString = null;
1193             if (pValue != null) {
1194                 myString = pValue.toString();
1195             }
1196             super.setObject(myString);
1197         }
1198 
1199         @Override
1200         protected void storeValue(final PreparedStatement pStatement,
1201                                   final int pIndex) throws SQLException {
1202             final String myValue = getValue();
1203             if (myValue != null) {
1204                 final BigDecimal myDecimal = new BigDecimal(myValue);
1205                 pStatement.setBigDecimal(pIndex, myDecimal);
1206             } else {
1207                 pStatement.setNull(pIndex, Types.DECIMAL);
1208             }
1209         }
1210 
1211         /**
1212          * Obtain the value.
1213          *
1214          * @param pFormatter the data formatter
1215          * @return the money value
1216          * @throws OceanusException on error
1217          */
1218         public OceanusRatio getValue(final OceanusDataFormatter pFormatter) throws OceanusException {
1219             try {
1220                 return pFormatter.parseValue(getValue(), OceanusRatio.class);
1221             } catch (IllegalArgumentException e) {
1222                 throw new PrometheusDataException(getValue(), "Bad Ratio Value", e);
1223             }
1224         }
1225     }
1226 
1227     /**
1228      * The binaryColumn Class.
1229      */
1230     protected static final class PrometheusBinaryColumn
1231             extends PrometheusColumnDefinition {
1232         /**
1233          * The length of the column.
1234          */
1235         private final int theLength;
1236 
1237         /**
1238          * Constructor.
1239          *
1240          * @param pTable  the table to which the column belongs
1241          * @param pId     the column id
1242          * @param pLength the length of the column
1243          */
1244         PrometheusBinaryColumn(final PrometheusTableDefinition pTable,
1245                                final MetisDataFieldId pId,
1246                                final int pLength) {
1247             /* Record the column type */
1248             super(pTable, pId);
1249             theLength = pLength;
1250         }
1251 
1252         @Override
1253         protected void buildColumnType(final StringBuilder pBuilder) {
1254             /* Add the column type */
1255             final PrometheusJDBCDriver myDriver = getDriver();
1256             pBuilder.append(myDriver.getDatabaseType(PrometheusColumnType.BINARY));
1257             if (myDriver.defineBinaryLength()) {
1258                 pBuilder.append("(");
1259                 pBuilder.append(theLength);
1260                 pBuilder.append(')');
1261             }
1262         }
1263 
1264         /**
1265          * Set the value.
1266          *
1267          * @param pValue the value
1268          */
1269         void setValue(final byte[] pValue) {
1270             super.setObject(pValue);
1271         }
1272 
1273         /**
1274          * Get the value.
1275          *
1276          * @return the value
1277          */
1278         byte[] getValue() {
1279             return (byte[]) super.getObject();
1280         }
1281 
1282         @Override
1283         protected void loadValue(final ResultSet pResults,
1284                                  final int pIndex) throws SQLException {
1285             final byte[] myValue = pResults.getBytes(pIndex);
1286             setValue(myValue);
1287         }
1288 
1289         @Override
1290         protected void storeValue(final PreparedStatement pStatement,
1291                                   final int pIndex) throws SQLException {
1292             pStatement.setBytes(pIndex, getValue());
1293         }
1294     }
1295 }