diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a2ded4678..d1c067c1f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,222 +1,218 @@ name: Test on: - push: - pull_request: - schedule: - # Run weekly on Saturday - - cron: '37 3 * * SAT' + push: + pull_request: + schedule: + # Run weekly on Saturday + - cron: "37 3 * * SAT" jobs: - mysql: - runs-on: ubuntu-latest - strategy: - fail-fast: false - max-parallel: 5 - matrix: - python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] - - services: - mariadb: - image: mariadb - env: - MARIADB_ROOT_PASSWORD: debug_toolbar - options: >- - --health-cmd "mariadb-admin ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 3306:3306 - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - allow-prereleases: true - - - name: Get pip cache dir - id: pip-cache - run: | - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - - - name: Cache - uses: actions/cache@v4 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: - ${{ matrix.python-version }}-v1-${{ hashFiles('**/pyproject.toml') }}-${{ hashFiles('**/tox.ini') }} - restore-keys: | - ${{ matrix.python-version }}-v1- - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade tox tox-gh-actions - - - name: Test with tox - run: tox - env: - DB_BACKEND: mysql - DB_USER: root - DB_PASSWORD: debug_toolbar - DB_HOST: 127.0.0.1 - DB_PORT: 3306 - - - postgres: - runs-on: ubuntu-latest - strategy: - fail-fast: false - max-parallel: 5 - matrix: - # Skip 3.13 here, it needs the psycopg3 / postgis3 database - python-version: ['3.9', '3.10', '3.11', '3.12'] - database: [postgresql, postgis] - # Add psycopg3 to our matrix for modern python versions - include: - - python-version: '3.10' - database: psycopg3 - - python-version: '3.11' - database: psycopg3 - - python-version: '3.12' - database: psycopg3 - - python-version: '3.13' - database: psycopg3 - - python-version: '3.13' - database: postgis3 - - services: - postgres: - image: postgis/postgis:14-3.1 - env: - POSTGRES_DB: debug_toolbar - POSTGRES_USER: debug_toolbar - POSTGRES_PASSWORD: debug_toolbar - ports: - - 5432:5432 - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - allow-prereleases: true - - - name: Get pip cache dir - id: pip-cache - run: | - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - - - name: Cache - uses: actions/cache@v4 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: - ${{ matrix.python-version }}-v1-${{ hashFiles('**/pyproject.toml') }}-${{ hashFiles('**/tox.ini') }} - restore-keys: | - ${{ matrix.python-version }}-v1- - - - name: Install gdal-bin (for postgis) - run: | - sudo apt-get -qq update - sudo apt-get -y install gdal-bin - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade tox tox-gh-actions - - - name: Test with tox - run: tox - env: - DB_BACKEND: ${{ matrix.database }} - DB_HOST: localhost - DB_PORT: 5432 - - sqlite: - runs-on: ubuntu-latest - strategy: - fail-fast: false - max-parallel: 5 - matrix: - python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - allow-prereleases: true - - - name: Get pip cache dir - id: pip-cache - run: | - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - - - name: Cache - uses: actions/cache@v4 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: - ${{ matrix.python-version }}-v1-${{ hashFiles('**/pyproject.toml') }}-${{ hashFiles('**/tox.ini') }} - restore-keys: | - ${{ matrix.python-version }}-v1- - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade tox tox-gh-actions - - - name: Test with tox - run: tox - env: - DB_BACKEND: sqlite3 - DB_NAME: ":memory:" - - lint: - runs-on: ubuntu-latest - strategy: - fail-fast: false - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: 3.9 - - - name: Get pip cache dir - id: pip-cache - run: | - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - - - name: Cache - uses: actions/cache@v4 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: - ${{ matrix.python-version }}-v1-${{ hashFiles('**/pyproject.toml') }}-${{ hashFiles('**/tox.ini') }} - restore-keys: | - ${{ matrix.python-version }}-v1- - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade tox - - - name: Test with tox - run: tox -e docs,packaging + mysql: + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 5 + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + + services: + mariadb: + image: mariadb + env: + MARIADB_ROOT_PASSWORD: debug_toolbar + options: >- + --health-cmd "mariadb-admin ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 3306:3306 + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + allow-prereleases: true + + - name: Get pip cache dir + id: pip-cache + run: | + echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT + + - name: Cache + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ matrix.python-version }}-v1-${{ hashFiles('**/pyproject.toml') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.python-version }}-v1- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox tox-gh-actions + + - name: Test with tox + run: tox + env: + DB_BACKEND: mysql + DB_USER: root + DB_PASSWORD: debug_toolbar + DB_HOST: 127.0.0.1 + DB_PORT: 3306 + + postgres: + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 5 + matrix: + # Skip 3.13 here, it needs the psycopg3 / postgis3 database + python-version: ["3.9", "3.10", "3.11", "3.12"] + database: [postgresql, postgis] + # Add psycopg3 to our matrix for modern python versions + include: + - python-version: "3.10" + database: psycopg3 + - python-version: "3.11" + database: psycopg3 + - python-version: "3.12" + database: psycopg3 + - python-version: "3.13" + database: psycopg3 + - python-version: "3.13" + database: postgis3 + + services: + postgres: + image: postgis/postgis:14-3.1 + env: + POSTGRES_DB: debug_toolbar + POSTGRES_USER: debug_toolbar + POSTGRES_PASSWORD: debug_toolbar + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + allow-prereleases: true + + - name: Get pip cache dir + id: pip-cache + run: | + echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT + + - name: Cache + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ matrix.python-version }}-v1-${{ hashFiles('**/pyproject.toml') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.python-version }}-v1- + + - name: Install gdal-bin (for postgis) + run: | + sudo apt-get -qq update + sudo apt-get -y install gdal-bin + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox tox-gh-actions + + - name: Test with tox + run: tox + env: + DB_BACKEND: ${{ matrix.database }} + DB_NAME: debug_toolbar + DB_USER: debug_toolbar + DB_PASSWORD: debug_toolbar + DB_HOST: localhost + DB_PORT: 5432 + + sqlite: + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 5 + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + allow-prereleases: true + + - name: Get pip cache dir + id: pip-cache + run: | + echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT + + - name: Cache + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ matrix.python-version }}-v1-${{ hashFiles('**/pyproject.toml') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.python-version }}-v1- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox tox-gh-actions + + - name: Test with tox + run: tox + env: + DB_BACKEND: sqlite3 + DB_NAME: ":memory:" + + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.9 + + - name: Get pip cache dir + id: pip-cache + run: | + echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT + + - name: Cache + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: 3.9-v1-${{ hashFiles('**/pyproject.toml') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + 3.9-v1- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox + + - name: Test with tox + run: tox -e docs,packaging diff --git a/debug_toolbar/panels/sql/forms.py b/debug_toolbar/panels/sql/forms.py index 44906924d..9e6934f05 100644 --- a/debug_toolbar/panels/sql/forms.py +++ b/debug_toolbar/panels/sql/forms.py @@ -102,6 +102,15 @@ def profile(self): query = self.cleaned_data["query"] sql = query["raw_sql"] params = json.loads(query["params"]) + vendor = query["vendor"] + + # Profiling is only supported on MySQL + if vendor != "mysql": + raise ValueError( + f"Profiling is not supported for {vendor}. " + "Profiling is only available for MySQL databases." + ) + with self.cursor as cursor: cursor.execute("SET PROFILING=1") # Enable profiling cursor.execute(sql, params) # Execute SELECT diff --git a/docs/changes.rst b/docs/changes.rst index 701f9d608..c16abad77 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -6,6 +6,9 @@ Pending * Added a note about the default password in ``make example``. * Removed logging about the toolbar failing to serialize a value into JSON. +* Fixed PostgreSQL compatibility issues: SQL profiling now properly detects + MySQL requirement and provides clear error messages for other databases. + Fixed CI configuration to use correct PostgreSQL credentials. 6.0.0 (2025-07-22) ------------------ diff --git a/tests/test_postgresql_compatibility.py b/tests/test_postgresql_compatibility.py new file mode 100644 index 000000000..077a6de30 --- /dev/null +++ b/tests/test_postgresql_compatibility.py @@ -0,0 +1,74 @@ +""" +Tests for PostgreSQL compatibility fixes +""" + +from django.db import connection +from django.test import TestCase + +from debug_toolbar.panels.sql.forms import SQLSelectForm + + +class PostgreSQLCompatibilityTestCase(TestCase): + """ + Test that PostgreSQL-specific compatibility issues are handled correctly. + """ + + def test_profiling_only_supported_on_mysql(self): + """ + Test that profiling raises appropriate error for non-MySQL databases. + """ + # Skip this test if we're actually using MySQL + if connection.vendor == "mysql": + self.skipTest("This test is for non-MySQL databases") + + # Create a form and mock the cleaned_data to test profile method + form = SQLSelectForm() + form.cleaned_data = { + "query": { + "vendor": connection.vendor, + "alias": "default", + "raw_sql": "SELECT 1", + "params": "[]", + } + } + + # Test that profiling raises ValueError for non-MySQL databases + with self.assertRaises(ValueError) as cm: + form.profile() + + self.assertIn("Profiling is not supported", str(cm.exception)) + self.assertIn(connection.vendor, str(cm.exception)) + + def test_profiling_mysql_check(self): + """ + Test that profiling only allows MySQL vendor. + """ + form = SQLSelectForm() + + # Test with PostgreSQL + form.cleaned_data = { + "query": { + "vendor": "postgresql", + "alias": "default", + "raw_sql": "SELECT 1", + "params": "[]", + } + } + + with self.assertRaises(ValueError) as cm: + form.profile() + self.assertIn("Profiling is not supported for postgresql", str(cm.exception)) + + # Test with SQLite + form.cleaned_data = { + "query": { + "vendor": "sqlite", + "alias": "default", + "raw_sql": "SELECT 1", + "params": "[]", + } + } + + with self.assertRaises(ValueError) as cm: + form.profile() + self.assertIn("Profiling is not supported for sqlite", str(cm.exception))