All Downloads are FREE. Search and download functionalities are using the official Maven repository.

migratedb.v1.integrationtest.database.Informix.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2022-2024 The MigrateDB contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package migratedb.v1.integrationtest.database

import com.informix.jdbcx.IfxDataSource
import migratedb.v1.core.internal.database.informix.InformixDatabaseType
import migratedb.v1.integrationtest.database.mutation.IndependentDatabaseMutation
import migratedb.v1.integrationtest.database.mutation.InformixCreateTableMutation
import migratedb.v1.integrationtest.util.base.Names
import migratedb.v1.integrationtest.util.base.SafeIdentifier
import migratedb.v1.integrationtest.util.base.work
import migratedb.v1.integrationtest.util.container.Lease
import migratedb.v1.integrationtest.util.container.SharedResources
import org.testcontainers.containers.GenericContainer
import org.testcontainers.containers.wait.strategy.Wait
import org.testcontainers.utility.DockerImageName
import java.time.Duration
import java.util.concurrent.Semaphore
import javax.sql.DataSource

enum class Informix(image: String) : DbSystem {
    V14_10("icr.io/informix/informix-developer-database:14.10.FC7W1DE"),
    ;

    // Relevant idiosyncrasies:
    //  - Does not support schemas as namespaces (CREATE SCHEMA just assigns the same owner to multiple DB objects)

    private val containerAlias = "informix_${name.lowercase()}"
    private val image = DockerImageName.parse(image)

    override fun toString() = "Informix ${name.replace('_', '.')}"

    companion object {
        private const val port = 9088
        private const val adminUser = "informix"
        private const val password = "in4mix"
    }

    class Container(image: DockerImageName) : GenericContainer(image) {
        fun dataSource(database: String? = null): DataSource {
            return IfxDataSource().also {
                it.user = adminUser
                it.password = password
                it.ifxIFXHOST = host
                it.portNumber = getMappedPort(port)
                if (database != null) {
                    it.databaseName = database
                }
            }
        }

        init {
            withEnv("LICENSE", "accept")
            withEnv("STORAGE", "local")
            withExposedPorts(port)
            waitingFor(Wait.forListeningPort())
        }
    }

    private val handles = Semaphore(5, true)

    /**
     * Informix seems allergic to more than 5 connections per container.
     */
    override fun get(sharedResources: SharedResources): DbSystem.Instance {
        val containerLease = sharedResources.container(containerAlias) { Container(image) }
        try {
            handles.acquire()
        } catch (e: InterruptedException) {
            containerLease.close()
            throw e
        }
        return Instance(containerLease)
    }

    private inner class Instance(private val container: Lease) : DbSystem.Instance {
        override val type = InformixDatabaseType()
        private var released = false
        private val internalDs by lazy { container().dataSource() }

        override fun createNamespaceIfNotExists(namespace: SafeIdentifier): SafeIdentifier? {
            internalDs.work(connectTimeout = Duration.ofMinutes(1)) {
                it.execute("create database if not exists $namespace with buffered log")
            }
            return null
        }

        override fun newAdminConnection(namespace: SafeIdentifier): DataSource {
            return container().dataSource(namespace.toString())
        }

        override fun dropNamespaceIfExists(namespace: SafeIdentifier) {
            internalDs.work {
                it.execute("drop database if exists $namespace")
            }
        }

        override fun nextMutation(schema: SafeIdentifier?): IndependentDatabaseMutation {
            check(schema == null) { "Informix does not support schemas" }
            return InformixCreateTableMutation(normalizeCase(Names.nextTable()))
        }

        override fun close() {
            container.use {
                synchronized(this) {
                    if (!released) {
                        released = true
                        handles.release()
                    }
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy