namespace InvercargillSql { /** * Static factory for creating database connections from connection strings. * * This factory maintains a registry of connection providers indexed by scheme. * The SQLite provider is automatically registered on first use. * * Example usage: * ```vala * // Simple connection * var conn = ConnectionFactory.create_and_open("sqlite:///mydb.sqlite"); * * // With options * var conn = ConnectionFactory.create_and_open("sqlite:///readonly.db?mode=ro"); * * // Register custom provider * ConnectionFactory.register_provider("postgresql", new PostgresProvider()); * var conn = ConnectionFactory.create_and_open("postgresql://user:pass@host/db"); * ``` */ public class ConnectionFactory : Object { private static bool _sqlite_registered = false; private static ConnectionProvider? _sqlite_provider = null; private static bool _postgres_registered = false; private static ConnectionProvider? _postgres_provider = null; /** * Creates a new database connection from a connection string. * * The connection is created but not opened. Call open() on the * returned connection to establish the database connection, or * use create_and_open() to create and open in one step. * * @param connection_string The connection string to parse * @return A new Connection instance (not opened) * @throws SqlError.INVALID_CONNECTION_STRING if the format is invalid * @throws SqlError.CONNECTION_FAILED if provider creation fails */ public static Connection create(string connection_string) throws SqlError { ConnectionString cs = ConnectionString.parse(connection_string); ConnectionProvider provider = get_provider(cs.scheme); return provider.create_connection(cs); } /** * Creates and opens a database connection from a connection string. * * This is a convenience method that combines create() and open(). * * @param connection_string The connection string to parse * @return A new Connection instance (opened) * @throws SqlError.INVALID_CONNECTION_STRING if the format is invalid * @throws SqlError.CONNECTION_FAILED if connection fails */ public static Connection create_and_open(string connection_string) throws SqlError { Connection conn = create(connection_string); conn.open(); return conn; } /** * Creates and opens a database connection asynchronously. * * @param connection_string The connection string to parse * @return A new Connection instance (opened) * @throws SqlError.INVALID_CONNECTION_STRING if the format is invalid * @throws SqlError.CONNECTION_FAILED if connection fails */ public async static Connection create_and_open_async(string connection_string) throws SqlError { ConnectionString cs = ConnectionString.parse(connection_string); ConnectionProvider provider = get_provider(cs.scheme); Connection conn = yield provider.create_connection_async(cs); conn.open(); return conn; } /** * Registers a connection provider for a scheme. * * If a provider is already registered for the scheme, it will be * replaced. * * @param scheme The scheme (e.g., "sqlite", "postgresql") * @param provider The provider instance */ public static void register_provider(string scheme, ConnectionProvider provider) { string scheme_lower = scheme.down(); if (scheme_lower == "sqlite") { _sqlite_provider = provider; _sqlite_registered = true; } else if (scheme_lower == "postgres" || scheme_lower == "postgresql") { _postgres_provider = provider; _postgres_registered = true; } // Other providers can be registered once a dictionary of // schemes is introduced; sqlite and postgres are the natives. } /** * Gets the provider for a scheme. * * Auto-registers SQLite provider on first access. * * @param scheme The scheme to look up * @return The ConnectionProvider for the scheme * @throws SqlError.INVALID_CONNECTION_STRING if no provider is registered */ private static ConnectionProvider get_provider(string scheme) throws SqlError { string scheme_lower = scheme.down(); if (scheme_lower == "sqlite") { if (!_sqlite_registered) { _sqlite_provider = new SqliteProvider(); _sqlite_registered = true; } return _sqlite_provider; } if (scheme_lower == "postgres" || scheme_lower == "postgresql") { if (!_postgres_registered) { _postgres_provider = new PostgresProvider(); _postgres_registered = true; } return _postgres_provider; } throw new SqlError.INVALID_CONNECTION_STRING( "No connection provider registered for scheme: %s".printf(scheme) ); } /** * Clears all registered providers. * * Useful for testing or when reconfiguring the factory. */ public static void clear_providers() { _sqlite_provider = null; _sqlite_registered = false; _postgres_provider = null; _postgres_registered = false; } /** * Checks if a provider is registered for a scheme. * * @param scheme The scheme to check * @return true if a provider is registered */ public static bool has_provider(string scheme) { string scheme_lower = scheme.down(); if (scheme_lower == "sqlite") { return _sqlite_registered; } if (scheme_lower == "postgres" || scheme_lower == "postgresql") { return _postgres_registered; } return false; } } }