<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by kandemirozenc on Medium]]></title>
        <description><![CDATA[Stories by kandemirozenc on Medium]]></description>
        <link>https://medium.com/@kandemirozenc?source=rss-1e849883b5ef------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*wNOiClEVOQ0S0Ma7f4oFRA.jpeg</url>
            <title>Stories by kandemirozenc on Medium</title>
            <link>https://medium.com/@kandemirozenc?source=rss-1e849883b5ef------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 25 May 2026 22:04:05 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@kandemirozenc/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[SQL Partitioning (Postgresql, Oracle) vs NoSQL Sharding: An In-Depth Comparison]]></title>
            <link>https://medium.com/@kandemirozenc/sql-partitioning-postgresql-oracle-vs-nosql-sharding-an-in-depth-comparison-c5813b795576?source=rss-1e849883b5ef------2</link>
            <guid isPermaLink="false">https://medium.com/p/c5813b795576</guid>
            <category><![CDATA[partition]]></category>
            <category><![CDATA[database-architecture]]></category>
            <category><![CDATA[sharding]]></category>
            <dc:creator><![CDATA[kandemirozenc]]></dc:creator>
            <pubDate>Sat, 23 Aug 2025 14:27:52 GMT</pubDate>
            <atom:updated>2025-08-23T14:27:52.047Z</atom:updated>
            <content:encoded><![CDATA[<h3>Introduction</h3><p>In today’s world, writing code is no longer the hard part — <em>anyone can do it</em>. The real challenge lies in building systems that are <strong>fast, scalable, and resilient</strong>. As modern applications generate massive volumes of data, database design becomes a critical factor in delivering high-performance, efficient applications.</p><p>Two important approaches stand out in tackling these challanges: <strong>SQL Partitioning</strong> and <strong>NoSQL Sharding</strong>. In this article, we’ll break them down from a technical perspective and explore when each should be your weapon of choice.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4aUBV86PbIkJSSAa7Rl0_g.png" /></figure><h3>PostgreSQL Partitioning</h3><p>Since version 10, PostgreSQL has offered native partitioning, making it easier to manage large tables. Partitioning divides a single logical table into smaller child tables (partitions) to improve performance.</p><h3>Types of Partitioning</h3><ul><li><strong>Range Partitioning:</strong> Split data by ranges (e.g., date ranges).</li><li><strong>List Partitioning:</strong> Split data by specific sets of values (e.g., country codes).</li><li><strong>Hash Partitioning:</strong> Evenly distribute data using a hash function.</li></ul><h3>Example</h3><pre>CREATE TABLE transactions (<br>    id BIGSERIAL PRIMARY KEY,<br>    store_id INT,<br>    created_at DATE,<br>    amount NUMERIC<br>) PARTITION BY RANGE (created_at);</pre><pre>CREATE TABLE transactions_2025_q1 PARTITION OF transactions<br>    FOR VALUES FROM (&#39;2025-01-01&#39;) TO (&#39;2025-04-01&#39;);</pre><pre>CREATE TABLE transactions_2025_q2 PARTITION OF transactions<br>    FOR VALUES FROM (&#39;2025-04-01&#39;) TO (&#39;2025-07-01&#39;);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/484/1*dKLkcDuodECAFzzgs5R31Q.png" /></figure><h3>Advantages</h3><ul><li>When query over table, it fetches data from related partition and improves query performance on large tables.</li><li>Indexes and constraints can be managed per partition.</li><li>Simplifies data archiving.</li></ul><h3>Disadvantages</h3><ul><li>Limited to a single database node (vertical scaling bottleneck).</li><li>Extremely large datasets may hit hardware limits.</li></ul><h3>Oracle Partitioning</h3><p>Oracle has supported advanced partitioning features since version 8i. With 11g, it introduced powerful options such as <strong>interval partitioning</strong>, <strong>reference partitioning</strong>, and <strong>composite (range-hash / range-list)</strong> partitioning.</p><h3>Partition Types (Oracle)</h3><ul><li><strong>Range Partitioning:</strong> Based on ranges of dates or numbers.</li><li><strong>List Partitioning:</strong> Based on defined value sets.</li><li><strong>Hash Partitioning:</strong> Even distribution using hashing.</li><li><strong>Interval Partitioning:</strong> Automatically creates partitions based on defined intervals (e.g., monthly).</li><li><strong>Composite Partitioning:</strong> Two-level partitioning (e.g., RANGE (date) + HASH (store_id)).</li><li><strong>Reference Partitioning:</strong> Child tables automatically inherit partitioning from the parent table.</li></ul><h3>Simple Range Example (Oracle)</h3><pre>CREATE TABLE transactions (<br>  id        NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,<br>  store_id  NUMBER NOT NULL,<br>  created_at DATE   NOT NULL,<br>  amount    NUMBER(12,2)<br>)<br>PARTITION BY RANGE (created_at) (<br>  PARTITION p2025q1 VALUES LESS THAN (DATE &#39;2025-04-01&#39;),<br>  PARTITION p2025q2 VALUES LESS THAN (DATE &#39;2025-07-01&#39;)<br>);</pre><pre>-- Local index: separate segment per partition<br>CREATE INDEX idx_transactions_store_created<br>  ON transactions (store_id, created_at)<br>  LOCAL;</pre><h3>Interval Partitioning (Automatic Monthly Partitions)</h3><pre>CREATE TABLE transactions_int (<br>  id         NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,<br>  store_id   NUMBER NOT NULL,<br>  created_at DATE   NOT NULL,<br>  amount     NUMBER(12,2)<br>)<br>PARTITION BY RANGE (created_at)<br>INTERVAL (NUMTOYMINTERVAL(1, &#39;MONTH&#39;))<br>(<br>  PARTITION p_base VALUES LESS THAN (DATE &#39;2025-01-01&#39;)<br>);<br>-- After 2025-01-01, a new partition is automatically created each month.</pre><h3>Composite Range-Hash (Balancing and Parallelism)</h3><pre>CREATE TABLE transactions_comp (<br>  id         NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,<br>  store_id   NUMBER NOT NULL,<br>  created_at DATE   NOT NULL,<br>  amount     NUMBER(12,2)<br>)<br>PARTITION BY RANGE (created_at)<br>SUBPARTITION BY HASH (store_id) SUBPARTITIONS 8<br>(<br>  PARTITION p2025q1 VALUES LESS THAN (DATE &#39;2025-04-01&#39;),<br>  PARTITION p2025q2 VALUES LESS THAN (DATE &#39;2025-07-01&#39;)<br>);</pre><h3>Common Administrative Operations</h3><pre>-- Split a partition<br>ALTER TABLE transactions SPLIT PARTITION p2025q2 AT (DATE &#39;2025-06-01&#39;)<br>  INTO (PARTITION p2025q2a, PARTITION p2025q2b);</pre><pre>-- Merge partitions<br>ALTER TABLE transactions MERGE PARTITIONS p2025q2a, p2025q2b INTO PARTITION p2025q2;</pre><pre>-- Move a partition to another tablespace<br>ALTER TABLE transactions MOVE PARTITION p2025q1 TABLESPACE ts_fast;</pre><pre>-- Exchange partition with staging table<br>ALTER TABLE transactions EXCHANGE PARTITION p2025q1 WITH TABLE transactions_stage WITHOUT VALIDATION;</pre><h3>PostgreSQL vs Oracle Partitioning: Quick Notes</h3><pre>+-------------------------+-----------------------------+-----------------------------+<br>| Feature                 | PostgreSQL                  | Oracle                      |<br>+-------------------------+-----------------------------+-----------------------------+<br>| Automatic Partitioning  | Not automatic; managed with | Native support with         |<br>|                         | cron/jobs or extensions     | INTERVAL partitioning       |<br>+-------------------------+-----------------------------+-----------------------------+<br>| Indexes                 | Separate indexes per        | Clear distinction between   |<br>|                         | partition; no global        | LOCAL and GLOBAL indexes    |<br>|                         | indexes                     |                             |<br>+-------------------------+-----------------------------+-----------------------------+<br>| Composite/Reference     | Supports composite;         | Mature support for composite|<br>|                         | reference partitioning      | and reference partitioning  |<br>|                         | limited                     |                             |<br>+-------------------------+-----------------------------+-----------------------------+<br>| Maintenance Operations  | Relies on ATTACH/DETACH and | Rich DDL: SPLIT, MERGE,     |<br>|                         | manual patterns             | MOVE, EXCHANGE              |<br>+-------------------------+-----------------------------+-----------------------------+<br>| Partition-wise Join /   | Pruning and predicate       | Advanced optimizer features;|<br>| Pruning                 | pushdown supported; less    | extensive partition-wise    |<br>|                         | advanced optimizer          | join capabilities           |<br>+-------------------------+-----------------------------+-----------------------------+<br></pre><h3>NoSQL Sharding</h3><p>Sharding splits data across multiple physical nodes to improve scalability. Popular NoSQL databases like MongoDB and Cassandra provide sharding as a core feature.</p><h3>How It Works</h3><ul><li><strong>Shard Key:</strong> Field that determines which node stores the data.</li><li><strong>Coordinator / Router:</strong> Directs queries to the correct shard.</li><li><strong>Replication:</strong> Each shard is replicated for fault tolerance.</li></ul><h3>Advantages</h3><ul><li>True horizontal scaling (add more nodes to increase capacity).</li><li>Can handle extremely large datasets.</li><li>Better distribution of workload.</li></ul><h3>Disadvantages</h3><ul><li>More complex architecture (routers, replication, rebalancing).</li><li>Joins and cross-shard queries can be expensive.</li></ul><p><strong>MongoDB (user-based sharding):</strong></p><pre>// Enable sharding for a database<br>sh.enableSharding(&quot;app&quot;)<br><br>// Shard a collection by hashed user_id<br>sh.shardCollection(&quot;app.users&quot;, { user_id: &quot;hashed&quot; })<br><br>// Query for a single user routes to one shard<br>use app<br>db.users.find({ user_id: 12345 })</pre><p><strong>Cassandra (IoT time-series sharding):</strong></p><pre>CREATE TABLE iot.events (<br>  device_id uuid,<br>  day       date,<br>  ts        timestamp,<br>  value     double,<br>  PRIMARY KEY ((device_id, day), ts)<br>);</pre><pre>-- Queries remain single-partition per device per day<br>SELECT * FROM iot.events WHERE device_id=? AND day=?;</pre><p>These simple examples illustrate how sharding keys (e.g., user_id, (device_id, day)) determine how data is distributed and how queries are routed efficiently.</p><h3>SQL Partitioning vs NoSQL Sharding</h3><pre><br>+--------------+-----------------------------+--------------------------------+<br>|   Feature    |      SQL Partitioning       |         NoSQL Sharding         |<br>+--------------+-----------------------------+--------------------------------+<br>| Approach     | Logical split within one DB | Split across multiple DB nodes |<br>| Scalability  | Limited by vertical scaling | Strong horizontal scaling      |<br>| Complexity   | Moderate                    | High                           |<br>| Join Support | Full                        | Limited / none                 |<br>| Use Cases    | Transactional workloads     | Big data, high-traffic systems |<br>+--------------+-----------------------------+--------------------------------+<br></pre><h3>Real-World Scenarios</h3><ul><li><strong>PostgreSQL Partitioning:</strong> An e-commerce platform with millions of transaction records. Partitioning by date accelerates reporting queries.</li><li><strong>Oracle Partitioning (Interval):</strong> A large bank’s customer transaction history table. Interval partitioning automatically creates monthly partitions, simplifying archiving.</li><li><strong>Oracle Partitioning (Composite):</strong> A telecom billing table. RANGE (billing period) + HASH (customer_id) composite partitioning improves both time-based queries and load balancing.</li><li><strong>Oracle Partitioning (Reference):</strong> Orders and order_items tables. Parent table is partitioned by order date, and child tables inherit the same structure automatically.</li><li><strong>NoSQL Sharding:</strong> A global social media platform. User data is distributed by user_id as the shard key, enabling millions of users to be served simultaneously.</li></ul><h3>Conclusion</h3><p>Partitioning and sharding address different aspects of scalability. If your workload is transactional and fits within a single database server, <strong>SQL Partitioning</strong> (PostgreSQL or Oracle) is often sufficient. For truly massive datasets or globally distributed systems, <strong>NoSQL Sharding</strong> is the better approach, it is related with your business requirements and database design choose. In practice, you may think about hybrid solutions that combine SQL partitioning with NoSQL sharding across regions are increasingly common.</p><ul><li><a href="https://www.postgresql.org/docs/current/ddl-partitioning.html">5.12. Table Partitioning</a></li><li><a href="https://www.oracle.com/database/technologies/partitioning.html">Partitioning Overview</a></li><li><a href="https://www.mongodb.com/docs/manual/sharding/">Sharding - Database Manual - MongoDB Docs</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c5813b795576" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding Interfaces, ABC, Protocol and Duck Typing in Python]]></title>
            <link>https://medium.com/@kandemirozenc/understanding-interfaces-abc-protocol-and-duck-typing-in-python-866ca32ab2a0?source=rss-1e849883b5ef------2</link>
            <guid isPermaLink="false">https://medium.com/p/866ca32ab2a0</guid>
            <category><![CDATA[duck-typing]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[abstract-base-classes]]></category>
            <category><![CDATA[protocol]]></category>
            <dc:creator><![CDATA[kandemirozenc]]></dc:creator>
            <pubDate>Sat, 07 Dec 2024 02:52:21 GMT</pubDate>
            <atom:updated>2024-12-07T02:52:21.906Z</atom:updated>
            <content:encoded><![CDATA[<h4>What is an Interface in Python?</h4><p>In Python, there is no direct equivalent of Java’s “interface,” but similar functionality can be achieved using the <strong>abc module (Abstract Base Classes)</strong>. Since Python embraces <strong>duck typing</strong>, enforcing a specific interface is not mandatory. However, for purposes like <strong>type checking</strong> or improving code readability, Python provides tools such as the abc module and <strong>Protocol (introduced in PEP 544)</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/438/1*ZGGK9e_jVniFxmmIzGg8Yw.png" /></figure><h4>1. Abstract Base Classes (ABC) as Interfaces</h4><p>The abc module allows you to define abstract base classes, which can enforce that subclasses implement specific methods.</p><pre>from abc import ABC, abstractmethod<br><br>class PaymentProcessor(ABC):<br>    @abstractmethod<br>    def process_payment(self, amount: float) -&gt; None:<br>        pass<br><br>class PayPalProcessor(PaymentProcessor):<br>    def process_payment(self, amount: float) -&gt; None:<br>        print(f&quot;Processing ${amount} payment via PayPal.&quot;)<br><br>processor = PayPalProcessor()<br>processor.process_payment(100.0)  # Valid</pre><p>If the subclass does not provide its own implementation of the process_payment method, it will raise an error and cannot be instantiated. This is because @abstractmethod enforces that all subclasses of PaymentProcessor must define the behavior of this method.</p><h4>2. Using Protocols (PEP 544)</h4><p>Introduced in <strong>Python 3.8</strong>, <strong>Protocol</strong> provides a lightweight and flexible way to define expected behaviors. Unlike abc, Protocols do not enforce method implementation at runtime. Instead, they are primarily used for <strong>static type checking</strong>.</p><pre>from typing import Protocol<br><br>class PaymentProcessor(Protocol):<br>    def process_payment(self, amount: float) -&gt; None:<br>        pass<br><br>class StripeProcessor:<br>    def process_payment(self, amount: float) -&gt; None:<br>        print(f&quot;Processing ${amount} payment via Stripe.&quot;)<br><br>class PayPalProcessor:<br>    def process_payment(self, amount: float) -&gt; None:<br>        print(f&quot;Processing ${amount} payment via PayPal.&quot;)<br><br>class CryptoProcessor:<br>    def process_payment(self, amount: float) -&gt; None:<br>        print(f&quot;Processing ${amount} payment via Cryptocurrency.&quot;)<br><br>def handle_payment(processor: PaymentProcessor, amount: float) -&gt; None:<br>    processor.process_payment(amount)<br><br># Usage<br>stripe = StripeProcessor()<br>paypal = PayPalProcessor()<br>crypto = CryptoProcessor()<br><br>handle_payment(stripe, 100.0)  # Output: Processing $100.0 payment via Stripe.<br>handle_payment(paypal, 200.0)  # Output: Processing $200.0 payment via PayPal.<br>handle_payment(crypto, 300.0)  # Output: Processing $300.0 payment via Cryptocurrency.</pre><h4>3. What is Duck Typing?</h4><p>Duck typing is a programming principle in Python (and other dynamic languages) that focuses on an object’s <strong>behavior</strong> rather than its <strong>type</strong>. If an object has the required methods or attributes, it is considered valid for use, regardless of its explicit type.</p><blockquote><strong><em>“If it walks like a duck and quacks like a duck, it must be a duck.”</em></strong></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/221/1*zZ39qaRZ7lbvi_L9NTFqVQ.png" /></figure><h4>Duck Typing in Action</h4><p>Instead of relying on type hierarchies, you check whether an object implements the necessary methods.</p><pre>class Dog:<br>    def speak(self):<br>        return &quot;Woof!&quot;<br><br>class Cat:<br>    def speak(self):<br>        return &quot;Meow!&quot;<br><br>def make_animal_speak(animal):<br>    # We don’t care about the type, just that the object has a &#39;speak&#39; method<br>    print(animal.speak())<br><br>dog = Dog()<br>cat = Cat()<br><br>make_animal_speak(dog)  # Output: Woof!<br>make_animal_speak(cat)  # Output: Meow!</pre><p>Here, the function make_animal_speak does not check the type of the object passed to it. It simply assumes that the object has a speak method.</p><h4>Benefits of Duck Typing</h4><ol><li><strong>Flexibility:</strong> Code is less tied to specific types, enabling reuse with different objects.</li><li><strong>Reduced Dependency:</strong> Functions rely on behavior rather than class inheritance.</li><li><strong>Polymorphism:</strong> Multiple classes can provide the same interface, allowing uniform handling of different objects.</li></ol><h4>Challenges with Duck Typing</h4><p>Because duck typing avoids explicit type checking, errors are only detected at <strong>runtime</strong>.</p><p><strong>Example of an Error:</strong></p><pre>class Fish:<br>    def swim(self):<br>        return &quot;I am swimming!&quot;<br><br># Fish lacks a &#39;speak&#39; method.<br>make_animal_speak(Fish())  # AttributeError: &#39;Fish&#39; object has no attribute &#39;speak&#39;</pre><h4>Safer Duck Typing</h4><p>To avoid runtime errors, you can combine duck typing with hasattr or modern <strong>type hinting</strong> mechanisms like Protocol.</p><pre>def make_animal_speak_safe(animal):<br>    if hasattr(animal, &#39;speak&#39;) and callable(animal.speak):<br>        print(animal.speak())<br>    else:<br>        raise TypeError(&quot;Object does not implement the &#39;speak&#39; method!&quot;)</pre><p>Or, using Protocol for type safety:</p><pre>from typing import Protocol<br><br>class Animal(Protocol):<br>    def speak(self) -&gt; str:<br>        pass<br><br>def make_animal_speak(animal: Animal):<br>    print(animal.speak())</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dUu1gtdcmZHw6AS-hsM0pA.png" /></figure><h3>Which One Should I Choose?</h3><ul><li><strong>Use Duck Typing</strong> when you prioritize simplicity and flexibility. It’s ideal for small projects or quick prototypes where enforcing strict rules is unnecessary. However, it lacks static type safety and can lead to runtime errors if misused.</li><li><strong>Use ABC</strong> when you need <strong>runtime enforcement</strong> of interface rules. It’s useful for critical applications where strict compliance with a defined interface is required, and you want to prevent instantiation of incomplete classes.</li><li><strong>Use Protocol</strong> for <strong>static type checking</strong> in modern Python projects. It’s the best choice when working with tools like mypy or when you want to combine the flexibility of duck typing with type safety.</li></ul><p>While <strong>Protocol Pattern</strong> and interfaces share some conceptual similarities, Python’s dynamic nature often makes <strong>duck typing sufficient</strong> for many use cases. Choose the approach based on the <strong>complexity, size, and type-safety requirements</strong> of your project.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=866ca32ab2a0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Testcontainers in Python for Integration Testing with MySQL]]></title>
            <link>https://medium.com/@kandemirozenc/testcontainers-in-python-for-integration-testing-with-mysql-63160a004fb5?source=rss-1e849883b5ef------2</link>
            <guid isPermaLink="false">https://medium.com/p/63160a004fb5</guid>
            <dc:creator><![CDATA[kandemirozenc]]></dc:creator>
            <pubDate>Tue, 29 Oct 2024 10:21:03 GMT</pubDate>
            <atom:updated>2024-10-29T10:33:22.926Z</atom:updated>
            <content:encoded><![CDATA[<p>Integration testing for applications that interact with databases or external services can be complex and time-consuming. The testcontainers library simplifies this by allowing developers to spin up isolated Docker containers within Python code, enabling easy setup and teardown. Best of all, it integrates smoothly with existing docker-compose configurations, making it ideal for testing real services like databases and message brokers.</p><p>Originally developed for Java and later adapted for Python, testcontainers has quickly become a popular choice for managing integration test dependencies efficiently and reliably. It ensures tests are realistic and maintainable by mirroring real environments, leading to more reliable results and greater confidence in your software&#39;s performance.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/599/1*whbgfs2p6xLDsVKCmz_DSQ.png" /></figure><h3>Prerequisites</h3><ul><li><strong>Python</strong>: Make sure Python 3.8+ is installed.</li><li><strong>Docker</strong>: Ensure Docker is installed and running to allow testcontainers to manage MySQL containers.</li></ul><p><strong>Example Scenario: Exporting a `.sql` File and Testing with MySQL</strong></p><p>In this example, we will:</p><p>1. Create an endpoint that exports a `.sql` file containing table creation and insertion commands.</p><p>2. Use `testcontainers` to create a temporary MySQL container.</p><p>3. Test that the `.sql` file content is compitable and correctly executed within the MySQL container.</p><p><strong>Step 1: Define the SQL Export Functionality</strong></p><p>To start, create an endpoint to export a .sql file. We’ll use FastAPI to define an endpoint that generates the file with basic SQL commands to create a table and insert data.</p><pre># router.py<br>from fastapi import APIRouter<br>from fastapi.responses import FileResponse<br>import tempfile<br><br>router = APIRouter()<br><br>def generate_sql_content() -&gt; str:<br>    return &#39;&#39;&#39;<br>    CREATE TABLE example_table (<br>        id INT PRIMARY KEY,<br>        name VARCHAR(100),<br>        age INT<br>    );<br><br>    INSERT INTO example_table (id, name, age) VALUES<br>    (1, &#39;Alice&#39;, 25),<br>    (2, &#39;Bob&#39;, 30),<br>    (3, &#39;Charlie&#39;, 35);<br>    &#39;&#39;&#39;<br><br>def create_temp_sql_file(content: str, suffix: str = &quot;.sql&quot;) -&gt; str:<br>    with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as temp_file:<br>        temp_file.write(content.encode(&quot;utf-8&quot;))<br>        return temp_file.name<br><br>@router.get(&quot;/export&quot;)<br>async def export_sql() -&gt; FileResponse:<br>    file_path = create_temp_sql_file(generate_sql_content())<br>    return FileResponse(path=file_path, media_type=&quot;application/sql&quot;, filename=&quot;exported_data.sql&quot;)</pre><p><strong>Step 2: Create the MySQL Container Fixture Using Testcontainers</strong></p><p>To set up MySQL for testing, we’ll create a pytest fixture using testcontainers. The fixtures will:</p><p>1. Start a MySQL container before tests.</p><p>2. Provide connection parameters to interact with the container.</p><pre># conftest.py<br>import pytest<br>from testcontainers.mysql import MySqlContainer<br>from mysql.connector.connection import MySQLConnection<br>import mysql.connector<br>from typing import Generator, Callable, cast<br><br>@pytest.fixture(scope=&quot;session&quot;)<br>def mysql_container() -&gt; Generator[MySqlContainer, None, None]:<br>&quot;&quot;&quot;Starts and provides a MySQL container instance, scoped to the session.&quot;&quot;&quot;<br>    with MySqlContainer(&quot;mysql:8.0&quot;) as mysql:<br>        yield mysql<br><br>@pytest.fixture(scope=&quot;session&quot;)<br>def get_db_connection(mysql_container: MySqlContainer) -&gt; Callable[[], MySQLConnection]:<br>&quot;&quot;&quot;Provides a function to establish a connection to the MySQL container.&quot;&quot;&quot;<br>    def _connect() -&gt; MySQLConnection:<br>        connection = mysql.connector.connect(<br>            host=mysql_container.get_container_host_ip(),<br>            port=mysql_container.get_exposed_port(3306),<br>            user=mysql_container.username,<br>            password=mysql_container.password,<br>            database=mysql_container.dbname,<br>        )<br>        return cast(MySQLConnection, connection)<br><br>    return _connect</pre><p><strong>Step 3: Write the Test for SQL Export and Execution</strong></p><p>Now that we have the export endpoint and MySQL container ready, let’s write a test to:</p><p>1. Download the `.sql` file from the endpoint.</p><p>2. Execute the SQL file in the MySQL container.</p><p>3. Verify the table and data.</p><pre># test_export_sql.py<br>import mysql.connector<br>from fastapi.testclient import TestClient<br>import os<br>from testcontainers_fastapi.main import app<br>from typing import Callable<br><br>client = TestClient(app)<br><br>def download_sql_file(endpoint: str = &quot;/export&quot;, filename: str = &quot;exported_data.sql&quot;) -&gt; str:<br>    response = client.get(endpoint)<br>    assert response.status_code == 200<br>    with open(filename, &quot;wb&quot;) as f:<br>        f.write(response.content)<br>    return filename<br><br>def execute_sql_file(cursor: mysql.connector.cursor.MySQLCursor, sql_file_path: str) -&gt; None:<br>    with open(sql_file_path, &quot;r&quot;) as file:<br>        sql_commands = file.read().split(&quot;;&quot;)<br>        for command in sql_commands:<br>            if command.strip():<br>                cursor.execute(command)<br><br>def test_export_sql_to_mysql(get_db_connection: Callable[[], mysql.connector.connection.MySQLConnection]) -&gt; None:<br>    sql_file_path = download_sql_file()<br><br>    connection = get_db_connection()<br>    try:<br>        cursor = connection.cursor()<br>        execute_sql_file(cursor, sql_file_path)<br>        connection.commit()<br><br>        cursor.execute(&quot;SELECT * FROM example_table;&quot;)<br>        rows = cursor.fetchall()<br>        expected_data = [(1, &quot;Alice&quot;, 25), (2, &quot;Bob&quot;, 30), (3, &quot;Charlie&quot;, 35)]<br>        assert rows == expected_data<br><br>    finally:<br>        cursor.close()<br>        connection.close()<br>        os.remove(sql_file_path)</pre><p><strong>Conclusion</strong></p><p>This setup shows how you can use testcontainers in Python to create an isolated MySQL instance for testing a `.sql` file export functionality. By using `testcontainers`, you can quickly spin up and tear down containers for each test session, making integration testing both reliable and convenient.</p><p>Full example you can find here: <a href="https://github.com/ozenck/testcontainers_fastapi">https://github.com/ozenck/testcontainers_fastapi</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=63160a004fb5" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>