H2DBDefinition.java

  1. /*
  2.  * Copyright 2013 Gregory Graham.
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *      http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package nz.co.gregs.dbvolution.databases.definitions;

  17. import nz.co.gregs.dbvolution.internal.query.LargeObjectHandlerType;
  18. import com.vividsolutions.jts.geom.Polygon;
  19. import java.text.SimpleDateFormat;
  20. import java.util.Date;
  21. import java.util.TimeZone;
  22. import nz.co.gregs.dbvolution.DBRow;
  23. import nz.co.gregs.dbvolution.databases.H2DB;
  24. import nz.co.gregs.dbvolution.databases.supports.SupportsPolygonDatatype;
  25. import nz.co.gregs.dbvolution.datatypes.*;
  26. import nz.co.gregs.dbvolution.datatypes.spatial2D.*;
  27. import nz.co.gregs.dbvolution.internal.h2.*;
  28. import nz.co.gregs.regexi.Regex;
  29. import nz.co.gregs.separatedstring.SeparatedString;
  30. import nz.co.gregs.separatedstring.SeparatedStringBuilder;

  31. /**
  32.  * Defines the features of the H2 database that differ from the standard
  33.  * database.
  34.  *
  35.  * <p>
  36.  * This DBDefinition is automatically included in {@link H2DB} instances, and
  37.  * you should not need to use it directly.
  38.  *
  39.  * @author Gregory Graham
  40.  */
  41. public class H2DBDefinition extends DBDefinition implements SupportsPolygonDatatype {

  42.     public static final long serialVersionUID = 1L;

  43.     private static final String DATE_FORMAT_STR = "yyyy-M-d HH:mm:ss.SSS Z";

  44.     private static SimpleDateFormat getStringToDateFormat() {
  45.         return new SimpleDateFormat(DATE_FORMAT_STR);
  46.     }

  47.     @Override
  48.     public String getDateFormattedForQuery(Date date) {
  49.         if (date == null) {
  50.             return getNull();
  51.         }
  52.         return "PARSEDATETIME('" + getStringToDateFormat().format(date) + "','" + DATE_FORMAT_STR + "')";
  53.     }

  54.     @Override
  55.     public String getDatePartsFormattedForQuery(String years, String months, String days, String hours, String minutes, String seconds, String subsecond, String timeZoneSign, String timeZoneHourOffset, String timeZoneMinuteOffSet) {
  56.         String result = "PARSEDATETIME("
  57.                 + "''||" + years
  58.                 + "||'-'||" + doLeftPadTransform(months, "'0'", "2")
  59.                 + "||'-'||" + doLeftPadTransform(days, "'0'", "2")
  60.                 + "||' '||" + doLeftPadTransform(hours, "'0'", "2")
  61.                 + "||':'||" + doLeftPadTransform(minutes, "'0'", "2")
  62.                 + "||':'||" + doIfThenElseTransform(doIntegerEqualsTransform(doStringLengthTransform("''||" + seconds), "1"), "'0'", "''")
  63.                 + "||" + seconds + "||' '||'" + timeZoneSign + "'"
  64.                 + "||" + doLeftPadTransform(timeZoneHourOffset, "'0'", "2")
  65.                 + "||" + doLeftPadTransform(timeZoneMinuteOffSet, "'0'", "2")
  66.                 + ", '" + "yyyy-M-d HH:mm:ss Z" + "'"
  67.                 + ")";
  68.         result = "TIMESTAMPADD('NANOSECOND', (" + subsecond + "*1000000000), " + result + ")";
  69.         return result;
  70.     }

  71.     @Override
  72.     public String getLocalDatePartsFormattedForQuery(String years, String months, String days, String hours, String minutes, String seconds, String subsecond, String timeZoneSign, String timeZoneHourOffset, String timeZoneMinuteOffSet) {
  73.         String result = "PARSEDATETIME("
  74.                 + "''||" + years
  75.                 + "||'-'||" + doLeftPadTransform(months, "'0'", "2")
  76.                 + "||'-'||" + doLeftPadTransform(days, "'0'", "2")
  77.                 + "||' '||" + doLeftPadTransform(hours, "'0'", "2")
  78.                 + "||':'||" + doLeftPadTransform(minutes, "'0'", "2")
  79.                 + "||':'||" + doIfThenElseTransform(doIntegerEqualsTransform(doStringLengthTransform("''||" + seconds), "1"), "'0'", "''")
  80.                 + "||" + seconds
  81.                 + ", '" + "yyyy-M-d HH:mm:ss" + "'"
  82.                 + ")";
  83.         result = "TIMESTAMPADD('NANOSECOND', (" + subsecond + "*1000000000), " + result + ")";
  84.         return result;
  85.     }

  86.     @Override
  87.     public String formatTableName(DBRow table) {
  88.         return table.getTableName().toUpperCase();
  89.     }

  90.     @Override
  91.     public String formatColumnName(String columnName) {
  92.         return "\"" + columnName.toUpperCase().replaceAll("\"", "\"\"") + "\"";
  93.     }

  94.     @Override
  95.     protected String getDatabaseDataTypeOfQueryableDatatype(QueryableDatatype<?> qdt) {
  96.         if (qdt instanceof DBInteger) {
  97.             return " BIGINT ";
  98.         } else if (qdt instanceof DBInstant) {
  99.             return "TIMESTAMP(9) WITH TIME ZONE";
  100.         } else if (qdt instanceof DBLocalDateTime) {
  101.             return "TIMESTAMP(9)";
  102.         } else if (qdt instanceof DBBoolean) {
  103.             return "BIT";
  104.         } else if (qdt instanceof DBBooleanArray) {
  105.             return "BOOLEAN ARRAY";
  106.         } else if (qdt instanceof DBDateRepeat) {
  107.             return DataTypes.DATEREPEAT.datatype();
  108.         } else if (qdt instanceof DBPoint2D) {
  109.             return DataTypes.POINT2D.datatype();
  110.         } else if (qdt instanceof DBLineSegment2D) {
  111.             return DataTypes.LINESEGMENT2D.datatype();
  112.         } else if (qdt instanceof DBLine2D) {
  113.             return DataTypes.LINE2D.datatype();
  114.         } else if (qdt instanceof DBPolygon2D) {
  115.             return DataTypes.POLYGON2D.datatype();
  116.         } else if (qdt instanceof DBMultiPoint2D) {
  117.             return DataTypes.MULTIPOINT2D.datatype();
  118.         } else {
  119.             return super.getDatabaseDataTypeOfQueryableDatatype(qdt);
  120.         }
  121.     }

  122.     @Override
  123.     public String doStringLengthTransform(String enclosedValue) {
  124.         return " CAST(" + getStringLengthFunctionName() + "( " + enclosedValue + " ) as NUMERIC(" + getNumericPrecision() + "," + getNumericScale() + "))";
  125.     }

  126.     @Override
  127.     public String doDateAtTimeZoneTransform(String dateSQL, TimeZone timeZone) throws UnsupportedOperationException {
  128.         throw new UnsupportedOperationException("H2DBDefinition does not support doDateAtTimeZoneTransform(String, TimeZone) yet.");
  129.     }

  130.     @Override
  131.     public String doDateAddDaysTransform(String dayValue, String numberOfDays) {
  132.         return "DATEADD('day'," + numberOfDays + ", " + dayValue + ")";
  133.     }

  134.     @Override
  135.     public String doDateAddSecondsTransform(String secondValue, String numberOfSeconds) {
  136.         return "DATEADD('second'," + numberOfSeconds + "," + secondValue + ")";
  137.     }

  138.     @Override
  139.     public String doDateAddMinutesTransform(String secondValue, String numberOfMinutes) {
  140.         return "DATEADD('minute'," + numberOfMinutes + "," + secondValue + ")";
  141.     }

  142.     @Override
  143.     public String doDateAddHoursTransform(String dateValue, String numberOfHours) {
  144.         return "DATEADD('hour'," + numberOfHours + "," + dateValue + ")";
  145.     }

  146.     @Override
  147.     public String doDateAddWeeksTransform(String dateValue, String numberOfWeeks) {
  148.         return "DATEADD('WEEK'," + numberOfWeeks + "," + dateValue + ")";
  149.     }

  150.     @Override
  151.     public String doDateAddMonthsTransform(String dateValue, String numberOfMonths) {
  152.         return "DATEADD('month'," + numberOfMonths + "," + dateValue + ")";
  153.     }

  154.     @Override
  155.     public String doDateAddYearsTransform(String dateValue, String numberOfYears) {
  156.         return "DATEADD('year'," + numberOfYears + "," + dateValue + ")";
  157.     }

  158.     @Override
  159.     public String doInstantAddDaysTransform(String instantValue, String numberOfDays) {
  160.         return doInsertInstantTimeZoneTransform(instantValue, "DATEADD('day'," + numberOfDays + ", " + doRemoveInstantTimeZoneTransform(instantValue) + ")");
  161.     }

  162.     @Override
  163.     public String doInstantAddSecondsTransform(String instantValue, String numberOfSeconds) {
  164.         return doInsertInstantTimeZoneTransform(instantValue, "DATEADD('second'," + numberOfSeconds + "," + doRemoveInstantTimeZoneTransform(instantValue) + ")");
  165.     }

  166.     @Override
  167.     public String doInstantAddMinutesTransform(String instantValue, String numberOfMinutes) {
  168.         return doInsertInstantTimeZoneTransform(instantValue, "DATEADD('minute'," + numberOfMinutes + "," + doRemoveInstantTimeZoneTransform(instantValue) + ")");
  169.     }

  170.     @Override
  171.     public String doInstantAddHoursTransform(String instantValue, String numberOfHours) {
  172.         return doInsertInstantTimeZoneTransform(instantValue, "DATEADD('hour'," + numberOfHours + "," + doRemoveInstantTimeZoneTransform(instantValue) + ")");
  173.     }

  174.     @Override
  175.     public String doInstantAddWeeksTransform(String instantValue, String numberOfWeeks) {
  176.         return doInsertInstantTimeZoneTransform(instantValue, "DATEADD('WEEK'," + numberOfWeeks + "," + doRemoveInstantTimeZoneTransform(instantValue) + ")");
  177.     }

  178.     @Override
  179.     public String doInstantAddMonthsTransform(String instantValue, String numberOfMonths) {
  180.         return doInsertInstantTimeZoneTransform(instantValue, "DATEADD('month'," + numberOfMonths + "," + doRemoveInstantTimeZoneTransform(instantValue) + ")");
  181.     }

  182.     @Override
  183.     public String doInstantAddYearsTransform(String instantValue, String numberOfYears) {
  184.         return doInsertInstantTimeZoneTransform(instantValue, "DATEADD('year'," + numberOfYears + "," + doRemoveInstantTimeZoneTransform(instantValue) + ")");
  185.     }

  186.     /**
  187.      * Defines the function used to get the current timestamp from the database.
  188.      *
  189.      * <p style="color: #F90;">Support DBvolution at
  190.      * <a href="http://patreon.com/dbvolution" target=new>Patreon</a></p>
  191.      *
  192.      * @return the H@ implementation subtracts the time zone from the current
  193.      * timestamp
  194.      */
  195.     @Override
  196.     protected String getCurrentDateTimeFunction() {
  197.         return " CURRENT_TIMESTAMP(9) ";
  198.     }

  199. //  @Override
  200. //  public String getDefaultTimeZoneSign() {
  201. //      return "case when extract(timezone_hour from current_timestamp(9))>=0 then '+' else '-' end";
  202. //  }
  203. //
  204. //  @Override
  205. //  public String getDefaultTimeZoneHour() {
  206. //      return "extract(timezone_hour from current_timestamp(9))";
  207. //  }
  208. //
  209. //  @Override
  210. //  public String getDefaultTimeZoneMinute() {
  211. //      return "extract(timezone_minute from current_timestamp(9))";
  212. //  }
  213.     @Override
  214.     public String doDayOfWeekTransform(String dateSQL) {
  215.         return " DAY_OF_WEEK(" + dateSQL + ")";
  216.     }

  217.     @Override
  218.     public String doInstantDayOfWeekTransform(String dateSQL) {
  219.         return " DAY_OF_WEEK(" + doComparableInstantTransform(dateSQL) + ")";
  220.     }

  221.     /**
  222.      * Returns the instant expression in the standard format that can be used to
  223.      * have consistent comparisons.
  224.      *
  225.      * <p>
  226.      * This generally adds the timezone back into the instant to convert it into a
  227.      * local datetime for databases, like H2, which have only partial support for
  228.      * Timestamp With Time Zone.</p>
  229.      *
  230.      * @param instantExpression the instant datatype expression to make comparable
  231.      * @return string the instantexpression converted into a comparable expression
  232.      */
  233.     @Override
  234.     public String doComparableInstantTransform(String instantExpression) {
  235.         return doRemoveInstantTimeZoneTransform(instantExpression);
  236.     }

  237.     @Override
  238.     public String doBooleanArrayTransform(Boolean[] bools) {
  239.         SeparatedString result = SeparatedStringBuilder.byCommaSpace().withPrefix("ARRAY[").withSuffix("]");
  240.         for (Boolean c : bools) {
  241.             result.add(c.toString().toUpperCase());
  242.         }
  243.         return result.toString();
  244.     }

  245.     @Override
  246.     public String doDateMinusToDateRepeatTransformation(String leftHandSide, String rightHandSide) {
  247.         return " " + DateRepeatFunctions.CREATE + "(" + leftHandSide + ", " + rightHandSide + ")";
  248.     }

  249.     @Override
  250.     public String doDatePlusDateRepeatTransform(String leftHandSide, String rightHandSide) {
  251.         return " " + DateRepeatFunctions.DATEADDITION + "(" + leftHandSide + ", " + rightHandSide + ")";
  252.     }

  253.     @Override
  254.     public String doDateMinusDateRepeatTransform(String leftHandSide, String rightHandSide) {
  255.         return " " + DateRepeatFunctions.DATESUBTRACTION + "(" + leftHandSide + ", " + rightHandSide + ")";
  256.     }

  257.     @Override
  258.     public String doDateRepeatEqualsTransform(String leftHandSide, String rightHandSide) {
  259.         return " " + DateRepeatFunctions.EQUALS + "(" + leftHandSide + ", " + rightHandSide + ")";
  260.     }

  261.     @Override
  262.     public String doDateRepeatNotEqualsTransform(String leftHandSide, String rightHandSide) {
  263.         return " " + DateRepeatFunctions.NOTEQUALS + "(" + leftHandSide + ", " + rightHandSide + ")";
  264.     }

  265.     @Override
  266.     public String doDateRepeatLessThanTransform(String leftHandSide, String rightHandSide) {
  267.         return DateRepeatFunctions.LESSTHAN + "(" + leftHandSide + ", " + rightHandSide + ")";
  268.     }

  269.     @Override
  270.     public String doDateRepeatLessThanEqualsTransform(String leftHandSide, String rightHandSide) {
  271.         return DateRepeatFunctions.LESSTHANEQUALS + "(" + leftHandSide + ", " + rightHandSide + ")";
  272.     }

  273.     @Override
  274.     public String doDateRepeatGreaterThanTransform(String leftHandSide, String rightHandSide) {
  275.         return DateRepeatFunctions.GREATERTHAN + "(" + leftHandSide + ", " + rightHandSide + ")";
  276.     }

  277.     @Override
  278.     public String doDateRepeatGreaterThanEqualsTransform(String leftHandSide, String rightHandSide) {
  279.         return DateRepeatFunctions.GREATERTHANEQUALS + "(" + leftHandSide + ", " + rightHandSide + ")";
  280.     }

  281.     @Override
  282.     public String doDateRepeatGetYearsTransform(String intervalStr) {
  283.         return DateRepeatFunctions.YEAR_PART + "(" + intervalStr + ")";
  284.     }

  285.     @Override
  286.     public String doDateRepeatGetMonthsTransform(String intervalStr) {
  287.         return DateRepeatFunctions.MONTH_PART + "(" + intervalStr + ")";
  288.     }

  289.     @Override
  290.     public String doDateRepeatGetDaysTransform(String intervalStr) {
  291.         return DateRepeatFunctions.DAY_PART + "(" + intervalStr + ")";
  292.     }

  293.     @Override
  294.     public String doDateRepeatGetHoursTransform(String intervalStr) {
  295.         return DateRepeatFunctions.HOUR_PART + "(" + intervalStr + ")";
  296.     }

  297.     @Override
  298.     public String doDateRepeatGetMinutesTransform(String intervalStr) {
  299.         return DateRepeatFunctions.MINUTE_PART + "(" + intervalStr + ")";
  300.     }

  301.     @Override
  302.     public String doDateRepeatGetSecondsTransform(String intervalStr) {
  303.         return DateRepeatFunctions.SECOND_PART + "(" + intervalStr + ")";
  304.     }

  305.     @Override
  306.     public String doLine2DAsTextTransform(String toSQLString) {
  307.         return Line2DFunctions.ASTEXT + "(" + toSQLString + ")";
  308.     }

  309.     @Override
  310.     public String doLine2DEqualsTransform(String toSQLString, String toSQLString0) {
  311.         return Line2DFunctions.EQUALS + "(" + toSQLString + ", " + toSQLString0 + ")";
  312.     }

  313.     @Override
  314.     public String doLine2DMeasurableDimensionsTransform(String toSQLString) {
  315.         return Line2DFunctions.DIMENSION + "(" + toSQLString + ")";
  316.     }

  317.     @Override
  318.     public String doLine2DGetBoundingBoxTransform(String toSQLString) {
  319.         return Line2DFunctions.BOUNDINGBOX + "(" + toSQLString + ")";
  320.     }

  321.     @Override
  322.     public String doLine2DGetMaxXTransform(String toSQLString) {
  323.         return Line2DFunctions.MAXX + "(" + toSQLString + ")";
  324.     }

  325.     @Override
  326.     public String doLine2DGetMinXTransform(String toSQLString) {
  327.         return Line2DFunctions.MINX + "(" + toSQLString + ")";
  328.     }

  329.     @Override
  330.     public String doLine2DGetMaxYTransform(String toSQLString) {
  331.         return Line2DFunctions.MAXY + "(" + toSQLString + ")";
  332.     }

  333.     @Override
  334.     public String doLine2DGetMinYTransform(String toSQLString) {
  335.         return Line2DFunctions.MINY + "(" + toSQLString + ")";
  336.     }

  337.     @Override
  338.     public String doLine2DIntersectsLine2DTransform(String toSQLString, String toSQLString0) {
  339.         return Line2DFunctions.INTERSECTS_LINE2D + "((" + toSQLString + "), (" + toSQLString0 + "))";
  340.     }

  341.     @Override
  342.     public String doLine2DIntersectionPointWithLine2DTransform(String toSQLString, String toSQLString0) {
  343.         return Line2DFunctions.INTERSECTIONWITH_LINE2D + "((" + toSQLString + "), (" + toSQLString0 + "))";
  344.     }

  345.     @Override
  346.     public String doLine2DAllIntersectionPointsWithLine2DTransform(String toSQLString, String toSQLString0) {
  347.         return Line2DFunctions.ALLINTERSECTIONSWITH_LINE2D + "((" + toSQLString + "), (" + toSQLString0 + "))";
  348.     }

  349.     @Override
  350.     public String doPoint2DEqualsTransform(String firstPoint, String secondPoint) {
  351.         return Point2DFunctions.EQUALS + "(" + firstPoint + ", " + secondPoint + ")";
  352.     }

  353.     @Override
  354.     public String doPoint2DGetXTransform(String toSQLString) {
  355.         return Point2DFunctions.GETX + "(" + toSQLString + ")";
  356.     }

  357.     @Override
  358.     public String doPoint2DGetYTransform(String toSQLString) {
  359.         return Point2DFunctions.GETY + "(" + toSQLString + ")";
  360.     }

  361.     @Override
  362.     public String doPoint2DMeasurableDimensionsTransform(String toSQLString) {
  363.         return Point2DFunctions.DIMENSION + "(" + toSQLString + ")";
  364.     }

  365.     @Override
  366.     public String doPoint2DGetBoundingBoxTransform(String toSQLString) {
  367.         return Point2DFunctions.BOUNDINGBOX + "(" + toSQLString + ")";
  368.     }

  369.     @Override
  370.     public String transformCoordinatesIntoDatabasePoint2DFormat(String xValue, String yValue) {
  371.         return "'POINT (" + xValue + " " + yValue + ")'";
  372.     }

  373.     @Override
  374.     public String doPoint2DAsTextTransform(String toSQLString) {
  375.         return Point2DFunctions.ASTEXT + "(" + toSQLString + ")";
  376.     }

  377.     @Override
  378.     public String transformPolygonIntoDatabasePolygon2DFormat(Polygon geom) {
  379.         String wktValue = geom.toText();
  380.         return Polygon2DFunctions.CREATE_FROM_WKTPOLYGON2D.alias() + "('" + wktValue + "')";
  381.     }

  382.     @Override
  383.     public String doPolygon2DAsTextTransform(String polygonSQL) {
  384.         return polygonSQL;
  385.     }

  386.     @Override
  387.     public String doPolygon2DEqualsTransform(String firstGeometry, String secondGeometry) {
  388.         return Polygon2DFunctions.EQUALS.alias() + "(" + firstGeometry + ", " + secondGeometry + ") ";
  389.     }

  390.     @Override
  391.     public String doPolygon2DUnionTransform(String firstGeometry, String secondGeometry) {
  392.         return Polygon2DFunctions.INTERSECTION.alias() + "(" + firstGeometry + ", " + secondGeometry + ")";
  393.     }

  394.     @Override
  395.     public String doPolygon2DIntersectionTransform(String firstGeometry, String secondGeometry) {
  396.         return Polygon2DFunctions.INTERSECTION.alias() + "(" + firstGeometry + ", " + secondGeometry + ")";
  397.     }

  398.     @Override
  399.     public String doPolygon2DIntersectsTransform(String firstGeometry, String secondGeometry) {
  400.         return Polygon2DFunctions.INTERSECTS.alias() + "(" + firstGeometry + ", " + secondGeometry + ")";
  401.     }

  402.     @Override
  403.     public String doPolygon2DContainsPolygon2DTransform(String firstGeometry, String secondGeometry) {
  404.         return Polygon2DFunctions.CONTAINS_POLYGON2D.alias() + "(" + firstGeometry + ", " + secondGeometry + ")";
  405.     }

  406.     @Override
  407.     public String doPolygon2DContainsPoint2DTransform(String polygon2DSQL, String point2DSQL) {
  408.         return Polygon2DFunctions.CONTAINS_POINT2D.alias() + "(" + polygon2DSQL + ", " + point2DSQL + ")";
  409.     }

  410.     @Override
  411.     public String doPolygon2DDoesNotIntersectTransform(String firstGeometry, String secondGeometry) {
  412.         return Polygon2DFunctions.DISJOINT.alias() + "(" + firstGeometry + ", " + secondGeometry + ")";
  413.     }

  414.     @Override
  415.     public String doPolygon2DOverlapsTransform(String firstGeometry, String secondGeometry) {
  416.         return Polygon2DFunctions.OVERLAPS.alias() + "(" + firstGeometry + ", " + secondGeometry + ")";
  417.     }

  418.     @Override
  419.     public String doPolygon2DTouchesTransform(String firstGeometry, String secondGeometry) {
  420.         return Polygon2DFunctions.TOUCHES.alias() + "(" + firstGeometry + ", " + secondGeometry + ")";
  421.     }

  422.     @Override
  423.     public String doPolygon2DWithinTransform(String firstGeometry, String secondGeometry) {
  424.         //indicate whether g1 is spatially within g2. This is the inverse of Contains().
  425.         // i.e. G1.within(G2) === G2.contains(G1)
  426.         return Polygon2DFunctions.WITHIN.alias() + "(" + firstGeometry + ", " + secondGeometry + ")";
  427.     }

  428.     @Override
  429.     public String doPolygon2DMeasurableDimensionsTransform(String toSQLString) {
  430.         return Polygon2DFunctions.DIMENSION.alias() + "(" + toSQLString + ")";
  431.     }

  432.     @Override
  433.     public String doPolygon2DGetBoundingBoxTransform(String toSQLString) {
  434.         return Polygon2DFunctions.BOUNDINGBOX.alias() + "(" + toSQLString + ")";
  435.     }

  436.     @Override
  437.     public String doPolygon2DGetAreaTransform(String toSQLString) {
  438.         return Polygon2DFunctions.AREA.alias() + "(" + toSQLString + ")";
  439.     }

  440.     @Override
  441.     public String doPolygon2DGetExteriorRingTransform(String toSQLString) {
  442.         return Polygon2DFunctions.EXTERIORRING.alias() + "(" + toSQLString + ")";
  443.     }

  444.     @Override
  445.     public String doPolygon2DGetMaxXTransform(String toSQLString) {
  446.         return Polygon2DFunctions.MAX_X.alias() + "(" + toSQLString + ")";
  447.     }

  448.     @Override
  449.     public String doPolygon2DGetMinXTransform(String toSQLString) {
  450.         return Polygon2DFunctions.MIN_Y.alias() + "(" + toSQLString + ")";
  451.     }

  452.     @Override
  453.     public String doPolygon2DGetMaxYTransform(String toSQLString) {
  454.         return Polygon2DFunctions.MAX_Y.alias() + "(" + toSQLString + ")";
  455.     }

  456.     @Override
  457.     public String doPolygon2DGetMinYTransform(String toSQLString) {
  458.         return Polygon2DFunctions.MIN_Y.alias() + "(" + toSQLString + ")";
  459.     }

  460.     @Override
  461.     public String doLineSegment2DIntersectsLineSegment2DTransform(String toSQLString, String toSQLString0) {
  462.         return LineSegment2DFunctions.INTERSECTS_LINESEGMENT2D + "((" + toSQLString + "), (" + toSQLString0 + "))";
  463.     }

  464.     @Override
  465.     public String doLineSegment2DGetMaxXTransform(String toSQLString) {
  466.         return LineSegment2DFunctions.MAXX + "(" + toSQLString + ")";
  467.     }

  468.     @Override
  469.     public String doLineSegment2DGetMinXTransform(String toSQLString) {
  470.         return LineSegment2DFunctions.MINX + "(" + toSQLString + ")";
  471.     }

  472.     @Override
  473.     public String doLineSegment2DGetMaxYTransform(String toSQLString) {
  474.         return LineSegment2DFunctions.MAXY + "(" + toSQLString + ")";
  475.     }

  476.     @Override
  477.     public String doLineSegment2DGetMinYTransform(String toSQLString) {
  478.         return LineSegment2DFunctions.MINY + "(" + toSQLString + ")";
  479.     }

  480.     @Override
  481.     public String doLineSegment2DGetBoundingBoxTransform(String toSQLString) {
  482.         return LineSegment2DFunctions.BOUNDINGBOX + "(" + toSQLString + ")";
  483.     }

  484.     @Override
  485.     public String doLineSegment2DDimensionTransform(String toSQLString) {
  486.         return LineSegment2DFunctions.DIMENSION + "(" + toSQLString + ")";
  487.     }

  488.     @Override
  489.     public String doLineSegment2DNotEqualsTransform(String toSQLString, String toSQLString0) {
  490.         return "!" + LineSegment2DFunctions.EQUALS + "((" + toSQLString + "), (" + toSQLString0 + "))";
  491.     }

  492.     @Override
  493.     public String doLineSegment2DEqualsTransform(String toSQLString, String toSQLString0) {
  494.         return LineSegment2DFunctions.EQUALS + "((" + toSQLString + "), (" + toSQLString0 + "))";
  495.     }

  496.     @Override
  497.     public String doLineSegment2DAsTextTransform(String toSQLString) {
  498.         return LineSegment2DFunctions.ASTEXT + "(" + toSQLString + ")";
  499.     }

  500.     @Override
  501.     public String doLineSegment2DIntersectionPointWithLineSegment2DTransform(String toSQLString, String toSQLString0) {
  502.         return LineSegment2DFunctions.INTERSECTIONPOINT_LINESEGMENT2D + "((" + toSQLString + "), (" + toSQLString0 + "))";
  503.     }

  504.     @Override
  505.     public String doMultiPoint2DEqualsTransform(String first, String second) {
  506.         return MultiPoint2DFunctions.EQUALS + "((" + first + "), (" + second + "), " + MultiPoint2DFunctions.getCurrentVersion() + ")";
  507.     }

  508.     @Override
  509.     public String doMultiPoint2DGetPointAtIndexTransform(String first, String index) {
  510.         return MultiPoint2DFunctions.GETPOINTATINDEX_FUNCTION + "((" + first + "), (" + index + "), " + MultiPoint2DFunctions.getCurrentVersion() + ")";
  511.     }

  512.     @Override
  513.     public String doMultiPoint2DGetNumberOfPointsTransform(String first) {
  514.         return MultiPoint2DFunctions.GETNUMBEROFPOINTS_FUNCTION + "(" + first + ", " + MultiPoint2DFunctions.getCurrentVersion() + ")";
  515.     }

  516.     @Override
  517.     public String doMultiPoint2DMeasurableDimensionsTransform(String first) {
  518.         return MultiPoint2DFunctions.DIMENSION + "(" + first + ", " + MultiPoint2DFunctions.getCurrentVersion() + ")";
  519.     }

  520.     @Override
  521.     public String doMultiPoint2DGetBoundingBoxTransform(String first) {
  522.         return MultiPoint2DFunctions.BOUNDINGBOX + "(" + first + ", " + MultiPoint2DFunctions.getCurrentVersion() + ")";
  523.     }

  524.     @Override
  525.     public String doMultiPoint2DAsTextTransform(String first) {
  526.         return MultiPoint2DFunctions.ASTEXT + "(" + first + ", " + MultiPoint2DFunctions.getCurrentVersion() + ")";
  527.     }

  528.     @Override
  529.     public String doMultiPoint2DToLine2DTransform(String first) {
  530.         return MultiPoint2DFunctions.ASLINE2D + "(" + first + ", " + MultiPoint2DFunctions.getCurrentVersion() + ")";
  531.     }

  532.     @Override
  533.     public String doMultiPoint2DGetMinYTransform(String first) {
  534.         return MultiPoint2DFunctions.MINY + "(" + first + ", " + MultiPoint2DFunctions.getCurrentVersion() + ")";
  535.     }

  536.     @Override
  537.     public String doMultiPoint2DGetMinXTransform(String first) {
  538.         return MultiPoint2DFunctions.MINX + "(" + first + ", " + MultiPoint2DFunctions.getCurrentVersion() + ")";
  539.     }

  540.     @Override
  541.     public String doMultiPoint2DGetMaxYTransform(String first) {
  542.         return MultiPoint2DFunctions.MAXY + "(" + first + ", " + MultiPoint2DFunctions.getCurrentVersion() + ")";
  543.     }

  544.     @Override
  545.     public String doMultiPoint2DGetMaxXTransform(String first) {
  546.         return MultiPoint2DFunctions.MAXX + "(" + first + ", " + MultiPoint2DFunctions.getCurrentVersion() + ")";
  547.     }

  548.     @Override
  549.     public LargeObjectHandlerType preferredLargeObjectWriter(DBLargeObject<?> lob) {
  550.         if (lob instanceof DBLargeText) {
  551.             return LargeObjectHandlerType.CLOB;
  552.         } else if (lob instanceof DBJavaObject) {
  553.             return LargeObjectHandlerType.BINARYSTREAM;
  554.         } else {
  555.             return super.preferredLargeObjectWriter(lob);
  556.         }
  557.     }

  558.     @Override
  559.     public LargeObjectHandlerType preferredLargeObjectReader(DBLargeObject<?> lob) {
  560.         if (lob instanceof DBLargeText) {
  561.             return LargeObjectHandlerType.BLOB;
  562.         } else if (lob instanceof DBJavaObject) {
  563.             return LargeObjectHandlerType.BINARYSTREAM;
  564.         } else {
  565.             return super.preferredLargeObjectReader(lob);
  566.         }
  567.     }

  568.     @Override
  569.     public boolean supportsFullOuterJoinNatively() {
  570.         return false;
  571.     }

  572.     @Override
  573.     public boolean supportsTableCheckingViaMetaData() {
  574.         return false;
  575.     }

  576.     @Override
  577.     public String doStringAccumulateTransform(String accumulateColumn, String separator, String referencedTable) {
  578.         return "GROUP_CONCAT(" + accumulateColumn + " SEPARATOR " + doStringLiteralWrapping(separator) + ")";
  579.     }

  580.     @Override
  581.     public String doStringAccumulateTransform(String accumulateColumn, String separator, String orderByColumnName, String referencedTable) {
  582.         return "GROUP_CONCAT(" + accumulateColumn + " ORDER BY " + orderByColumnName + " SEPARATOR " + doStringLiteralWrapping(separator) + ")";
  583.     }

  584.     /**
  585.      * Creates the CURRENTTIME function for this database.
  586.      *
  587.      * <p style="color: #F90;">Support DBvolution at
  588.      * <a href="http://patreon.com/dbvolution" target=new>Patreon</a></p>
  589.      *
  590.      * @return the default implementation returns " CURRENT_TIMESTAMP "
  591.      */
  592.     @Override
  593.     public String doCurrentUTCTimeTransform() {
  594.         return "dateadd(timezone_minute, -1*extract(timezone_minute from current_timestamp(9)), dateadd(timezone_hour, -1*extract(timezone_hour from current_timestamp(9)), dateadd(minute, -1*extract(timezone_minute from current_timestamp(9)), dateadd(hour, -1*extract(timezone_hour from current_timestamp(9)), current_timestamp(9)))))";
  595.     }

  596.     @Override
  597.     public String doCurrentUTCDateTimeTransform() {
  598.         return "dateadd(timezone_minute, -1*extract(timezone_minute from current_timestamp(9)), dateadd(timezone_hour, -1*extract(timezone_hour from current_timestamp(9)), dateadd(minute, -1*extract(timezone_minute from current_timestamp(9)), dateadd(hour, -1*extract(timezone_hour from current_timestamp(9)), current_timestamp(9)))))";
  599.     }

  600.     public String doRemoveInstantTimeZoneTransform(String instantValue) {
  601.         return "dateadd(minute, -1*extract(timezone_minute from " + instantValue + "), dateadd(hour, -1*extract(timezone_hour from " + instantValue + "), " + instantValue + "))/*!remove timezone*/";
  602.     }

  603.     public String doInsertInstantTimeZoneTransform(String instantValueWithCorrectTZ, String dateValue) {
  604.         return "dateadd(minute, extract(timezone_minute from " + instantValueWithCorrectTZ + "), dateadd(hour, extract(timezone_hour from " + instantValueWithCorrectTZ + "), " + dateValue + "))/*!insert timezone*/";
  605.     }

  606.     @Override
  607.     public boolean supportsDateRepeatDatatypeFunctions() {
  608.         return true;
  609.     }

  610.     @Override
  611.     public GroupByClauseMethod[] preferredGroupByClauseMethod() {
  612.         return new GroupByClauseMethod[]{GroupByClauseMethod.GROUPBYEXPRESSION, GroupByClauseMethod.SELECTEXPRESSION, GroupByClauseMethod.ALIAS, GroupByClauseMethod.INDEX};
  613.     }

  614.     private static final Regex DUPLICATE_COLUMN_EXCEPTION
  615.             = Regex
  616.                     .startingAnywhere()
  617.                     .literalCaseInsensitive("Duplicate column name \"")
  618.                     .anyCharacterExcept("\"").atLeastOnce()
  619.                     .literal("\";")
  620.                     .toRegex();

  621.     @Override
  622.     public boolean isDuplicateColumnException(Exception exc) {
  623.         return DUPLICATE_COLUMN_EXCEPTION.matchesWithinString(exc.getMessage());
  624.     }

  625.     private static final Regex PRIMARY_KEY_ALREADY_EXISTS = Regex
  626.             .empty()
  627.             .literalCaseInsensitive("Unique index or primary key violation:")
  628.             .toRegex();

  629.     @Override
  630.     public boolean isPrimaryKeyAlreadyExistsException(Exception alreadyExists) {
  631.         Throwable exc = alreadyExists;
  632.         while (exc != null) {
  633.             if ((exc instanceof org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException)
  634.                     || PRIMARY_KEY_ALREADY_EXISTS.matchesWithinString(exc.getMessage())) {
  635.                 return true;
  636.             }
  637.             exc = exc.getCause();
  638.         }
  639.         return super.isPrimaryKeyAlreadyExistsException(alreadyExists);
  640.     }

  641.     @Override
  642.     public String getSequenceUpdateSQL(String tableName, String columnName, long primaryKeyGenerated) {
  643.         return "ALTER TABLE IF EXISTS " + tableName + " ON COLUMN IF EXISTS " + columnName + " RESTART WITH " + (primaryKeyGenerated + 1);
  644.        
  645.     }
  646. }