From 008807b427623acaf14939593bdb7b9ae6586eec Mon Sep 17 00:00:00 2001 From: Emilio Date: Mon, 18 Sep 2023 16:02:17 +0000 Subject: [PATCH] GEOMESA-3295 Partitioned PostGIS - default to using prepared statements (#2993) --- docs/user/upgrade.rst | 7 ++++++ .../PartitionedPostgisDataStoreFactory.scala | 12 ++++++--- .../PartitionedPostgisDataStoreParams.scala | 9 +++++++ .../PartitionedPostgisDataStoreTest.scala | 25 ++++++++++++++++++- 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/docs/user/upgrade.rst b/docs/user/upgrade.rst index 090bf63e902e..f63872ae09d2 100644 --- a/docs/user/upgrade.rst +++ b/docs/user/upgrade.rst @@ -107,6 +107,13 @@ The following classes have been deprecated and will be removed in a future versi * org.locationtech.geomesa.kafka.confluent.SchemaParser.GeoMesaAvroDeserializableEnumProperty +Partitioned PostGIS Prepared Statements +--------------------------------------- + +If not specified, prepared statements now default to ``true`` in the partitioned PostGIS data store. Prepared +statements are generally faster on insert, and some attribute types (such as list-type attributes) are only +supported through prepared statements. + Version 4.0.0 Upgrade Guide +++++++++++++++++++++++++++ diff --git a/geomesa-gt/geomesa-gt-partitioning/src/main/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreFactory.scala b/geomesa-gt/geomesa-gt-partitioning/src/main/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreFactory.scala index 14dc1458cf44..d8eea4f256ba 100644 --- a/geomesa-gt/geomesa-gt-partitioning/src/main/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreFactory.scala +++ b/geomesa-gt/geomesa-gt-partitioning/src/main/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreFactory.scala @@ -16,7 +16,7 @@ import org.locationtech.geomesa.gt.partition.postgis.dialect.{PartitionedPostgis class PartitionedPostgisDataStoreFactory extends PostgisNGDataStoreFactory with LazyLogging { - import PartitionedPostgisDataStoreParams.{DbType, IdleInTransactionTimeout} + import PartitionedPostgisDataStoreParams.{DbType, IdleInTransactionTimeout, PreparedStatements} override def getDisplayName: String = "PostGIS (partitioned)" @@ -26,7 +26,7 @@ class PartitionedPostgisDataStoreFactory extends PostgisNGDataStoreFactory with override protected def setupParameters(parameters: java.util.Map[String, AnyRef]): Unit = { super.setupParameters(parameters) - Seq(DbType, IdleInTransactionTimeout) + Seq(DbType, IdleInTransactionTimeout, PreparedStatements) .foreach(p => parameters.put(p.key, p)) } @@ -44,8 +44,12 @@ class PartitionedPostgisDataStoreFactory extends PostgisNGDataStoreFactory with source } - override protected def createDataStoreInternal(store: JDBCDataStore, params: java.util.Map[String, _]): JDBCDataStore = { - + override protected def createDataStoreInternal(store: JDBCDataStore, baseParams: java.util.Map[String, _]): JDBCDataStore = { + val params = new java.util.HashMap[String, Any](baseParams) + // default to using prepared statements, if not specified + if (!params.containsKey(PreparedStatements.key)) { + params.put(PreparedStatements.key, java.lang.Boolean.TRUE) + } val ds = super.createDataStoreInternal(store, params) val dialect = new PartitionedPostgisDialect(ds) diff --git a/geomesa-gt/geomesa-gt-partitioning/src/main/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreParams.scala b/geomesa-gt/geomesa-gt-partitioning/src/main/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreParams.scala index 515e6b3289de..174d64bdc444 100644 --- a/geomesa-gt/geomesa-gt-partitioning/src/main/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreParams.scala +++ b/geomesa-gt/geomesa-gt-partitioning/src/main/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreParams.scala @@ -26,6 +26,15 @@ object PartitionedPostgisDataStoreParams { Collections.singletonMap(Parameter.LEVEL, "program") ) + val PreparedStatements = + new Param( + "preparedStatements", + classOf[java.lang.Boolean], + "Use prepared statements", + false, + java.lang.Boolean.FALSE + ) + val IdleInTransactionTimeout = new Param( "idle_in_transaction_session_timeout", diff --git a/geomesa-gt/geomesa-gt-partitioning/src/test/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreTest.scala b/geomesa-gt/geomesa-gt-partitioning/src/test/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreTest.scala index befca791c034..7f2288e78b51 100644 --- a/geomesa-gt/geomesa-gt-partitioning/src/test/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreTest.scala +++ b/geomesa-gt/geomesa-gt-partitioning/src/test/scala/org/locationtech/geomesa/gt/partition/postgis/PartitionedPostgisDataStoreTest.scala @@ -18,7 +18,7 @@ import org.junit.runner.RunWith import org.locationtech.geomesa.filter.FilterHelper import org.locationtech.geomesa.gt.partition.postgis.dialect.procedures.{DropAgedOffPartitions, PartitionMaintenance, RollWriteAheadLog} import org.locationtech.geomesa.gt.partition.postgis.dialect.tables.UserDataTable -import org.locationtech.geomesa.gt.partition.postgis.dialect.{PartitionedPostgisDialect, TableConfig, TypeInfo} +import org.locationtech.geomesa.gt.partition.postgis.dialect.{PartitionedPostgisDialect, PartitionedPostgisPsDialect, TableConfig, TypeInfo} import org.locationtech.geomesa.utils.collection.SelfClosingIterator import org.locationtech.geomesa.utils.geotools.{FeatureUtils, SimpleFeatureTypes} import org.locationtech.geomesa.utils.io.WithClose @@ -407,6 +407,29 @@ class PartitionedPostgisDataStoreTest extends Specification with BeforeAfterAll } } + "default to using prepared statements" in { + foreach(Seq(params, params + ("preparedStatements" -> "true"), params - "preparedStatements")) { params => + val ds = DataStoreFinder.getDataStore(params.asJava) + ds must not(beNull) + try { + ds must beAnInstanceOf[JDBCDataStore] + ds.asInstanceOf[JDBCDataStore].getSQLDialect must beAnInstanceOf[PartitionedPostgisPsDialect] + } finally { + ds.dispose() + } + } + foreach(Seq(params + ("preparedStatements" -> "false"))) { params => + val ds = DataStoreFinder.getDataStore(params.asJava) + ds must not(beNull) + try { + ds must beAnInstanceOf[JDBCDataStore] + ds.asInstanceOf[JDBCDataStore].getSQLDialect must beAnInstanceOf[PartitionedPostgisDialect] // not partitioned + } finally { + ds.dispose() + } + } + } + "support idle_in_transaction_session_timeout" in { val sft = SimpleFeatureTypes.renameSft(this.sft, "timeout")