Adding upstream version 1.1.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
a7c3cb7344
commit
7269eaf22a
23 changed files with 4597 additions and 0 deletions
47
.github/workflows/publish.yml
vendored
Normal file
47
.github/workflows/publish.yml
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
name: Build and Publish Package
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
types:
|
||||
- closed
|
||||
|
||||
jobs:
|
||||
publish-package:
|
||||
if: ${{ github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/v') }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out the main branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
with:
|
||||
version: 1.6.1
|
||||
- name: Configure poetry
|
||||
run: poetry config --no-interaction pypi-token.pypi ${{ secrets.PYPI_TOKEN }}
|
||||
- name: Get this package's Version
|
||||
id: package_version
|
||||
run: echo "package_version=$(poetry version --short)" >> $GITHUB_OUTPUT
|
||||
- name: Build package
|
||||
run: poetry build --no-interaction
|
||||
- name: Publish package to PyPI
|
||||
run: poetry publish --no-interaction
|
||||
- name: Create a Github Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: v${{ steps.package_version.outputs.package_version }}
|
||||
target_commitish: main
|
||||
token: ${{ secrets.GH_RELEASE_TOKEN }}
|
||||
body_path: CHANGELOG.md
|
||||
files: |
|
||||
LICENSE
|
||||
dist/*harlequin*.whl
|
||||
dist/*harlequin*.tar.gz
|
57
.github/workflows/release.yml
vendored
Normal file
57
.github/workflows/release.yml
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
name: Create Release Branch
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
newVersion:
|
||||
description: A version number for this release (e.g., "0.1.0")
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
prepare-release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Check out the main branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
with:
|
||||
version: 1.6.1
|
||||
- name: Create release branch
|
||||
run: |
|
||||
git checkout -b release/v${{ github.event.inputs.newVersion }}
|
||||
git push --set-upstream origin release/v${{ github.event.inputs.newVersion }}
|
||||
- name: Bump version
|
||||
run: poetry version ${{ github.event.inputs.newVersion }} --no-interaction
|
||||
- name: Ensure package can be built
|
||||
run: poetry build --no-interaction
|
||||
- name: Update CHANGELOG
|
||||
uses: thomaseizinger/keep-a-changelog-new-release@v1
|
||||
with:
|
||||
version: ${{ github.event.inputs.newVersion }}
|
||||
- name: Commit Changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: Bumps version to ${{ github.event.inputs.newVersion }}
|
||||
- name: Create pull request into main
|
||||
uses: thomaseizinger/create-pull-request@1.3.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
head: release/v${{ github.event.inputs.newVersion }}
|
||||
base: main
|
||||
title: v${{ github.event.inputs.newVersion }}
|
||||
body: >
|
||||
This PR was automatically generated. It bumps the version number
|
||||
in pyproject.toml and updates CHANGELOG.md. You may have to close
|
||||
this PR and reopen it to get the required checks to run.
|
161
.gitignore
vendored
Normal file
161
.gitignore
vendored
Normal file
|
@ -0,0 +1,161 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
Pipfile
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
10
.harlequin.toml
Normal file
10
.harlequin.toml
Normal file
|
@ -0,0 +1,10 @@
|
|||
default_profile = "azure"
|
||||
[profiles.azure]
|
||||
adapter = "mysql"
|
||||
theme = "harlequin"
|
||||
limit = 10000
|
||||
host = "harlequin-mysql-dev.mysql.database.azure.com"
|
||||
port = "3306"
|
||||
database = "dev"
|
||||
user = "harlequin"
|
||||
password = "roEj5cN9Jdyqbyqf"
|
79
CHANGELOG.md
Normal file
79
CHANGELOG.md
Normal file
|
@ -0,0 +1,79 @@
|
|||
# harlequin-mysql CHANGELOG
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.1.0] - 2025-01-28
|
||||
|
||||
- Bumps the MySQL Connector Python version to >=9.1
|
||||
- Bumps the required Harlequin version to >= 1.25.0
|
||||
- Adds support for the `openid_token_file` connection option introduced with MySQL Connector 9.1
|
||||
- This adapter now lazy-loads the catalog, which will dramatically improve the catalog performance for large databases with thousands of objects.
|
||||
- This adapter now implements interactions for catalog items, like dropping tables, inserting columns at the cursor, etc.
|
||||
|
||||
## [1.0.0] - 2025-01-07
|
||||
|
||||
- Drops support for Python 3.8
|
||||
- Adds support for Python 3.13
|
||||
- Adds support for Harlequin 2.X
|
||||
|
||||
## [0.3.0] - 2024-08-20
|
||||
|
||||
- Implements `connection_id` for better persistence.
|
||||
- Implements the `cancel()` protocol to cancel in-flight queries.
|
||||
- Implements `close()`
|
||||
- Fixes a bug where a race condition could cause a crash with an `AssertionError` ([#14](https://github.com/tconbeer/harlequin-mysql/issues/14) - thank you [@blasferna](https://github.com/blasferna)!).
|
||||
|
||||
## [0.2.0] - 2024-04-11
|
||||
|
||||
### Features
|
||||
|
||||
- Adds a `pool-size` CLI option to set the size of the MySQL connection pool. Defaults to 5.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Updates the connection pool config to keep all connections in sync after running a `use database` command ([#11](https://github.com/tconbeer/harlequin-mysql/issues/11) - thank you [@mlopezgva](https://github.com/mlopezgva)!).
|
||||
- Handles several issues caused by running too many concurrent queries and not fetching results.
|
||||
|
||||
## [0.1.3] - 2024-01-29
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fixes a typo in the help text for the `--user` option (thank you [@alexmalins](https://github.com/alexmalins)!).
|
||||
|
||||
## [0.1.2] - 2024-01-25
|
||||
|
||||
### Fixes
|
||||
|
||||
- Sets the `pool_name` property on the MySQL connection to prevent auto-generated pool names from being too long ([#6](https://github.com/tconbeer/harlequin-mysql/issues/6) - thank you [sondeokhyeon](https://github.com/sondeokhyeon)!).
|
||||
|
||||
## [0.1.1] - 2024-01-09
|
||||
|
||||
### Fixes
|
||||
|
||||
- Sorts relation names alphabetically and columns by ordinal position.
|
||||
|
||||
## [0.1.0] - 2023-12-14
|
||||
|
||||
### Features
|
||||
|
||||
- Adds a basic MySQL adapter with most common connection options.
|
||||
|
||||
[Unreleased]: https://github.com/tconbeer/harlequin-mysql/compare/1.1.0...HEAD
|
||||
|
||||
[1.1.0]: https://github.com/tconbeer/harlequin-mysql/compare/1.0.0...1.1.0
|
||||
|
||||
[1.0.0]: https://github.com/tconbeer/harlequin-mysql/compare/0.3.0...1.0.0
|
||||
|
||||
[0.3.0]: https://github.com/tconbeer/harlequin-mysql/compare/0.2.0...0.3.0
|
||||
|
||||
[0.2.0]: https://github.com/tconbeer/harlequin-mysql/compare/0.1.3...0.2.0
|
||||
|
||||
[0.1.3]: https://github.com/tconbeer/harlequin-mysql/compare/0.1.2...0.1.3
|
||||
|
||||
[0.1.2]: https://github.com/tconbeer/harlequin-mysql/compare/0.1.1...0.1.2
|
||||
|
||||
[0.1.1]: https://github.com/tconbeer/harlequin-mysql/compare/0.1.0...0.1.1
|
||||
|
||||
[0.1.0]: https://github.com/tconbeer/harlequin-mysql/compare/f2caef7de11e68bb2b9798fb597c3fc05044b71e...0.1.0
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Ted Conbeer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
18
Makefile
Normal file
18
Makefile
Normal file
|
@ -0,0 +1,18 @@
|
|||
.PHONY: check
|
||||
check:
|
||||
ruff format .
|
||||
ruff check . --fix
|
||||
mypy
|
||||
pytest
|
||||
|
||||
.PHONY: init
|
||||
init:
|
||||
docker-compose up -d
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
docker-compose down
|
||||
|
||||
.PHONY: serve
|
||||
serve:
|
||||
harlequin -P None -a mysql -h localhost -U root --password example --database dev
|
67
README.md
Normal file
67
README.md
Normal file
|
@ -0,0 +1,67 @@
|
|||
# harlequin-mysql
|
||||
|
||||
This repo provides the Harlequin adapter for MySQL.
|
||||
|
||||
## Installation
|
||||
|
||||
`harlequin-mysql` depends on `harlequin`, so installing this package will also install Harlequin.
|
||||
|
||||
### Using pip
|
||||
|
||||
To install this adapter into an activated virtual environment:
|
||||
```bash
|
||||
pip install harlequin-mysql
|
||||
```
|
||||
|
||||
### Using poetry
|
||||
|
||||
```bash
|
||||
poetry add harlequin-mysql
|
||||
```
|
||||
|
||||
### Using pipx
|
||||
|
||||
If you do not already have Harlequin installed:
|
||||
|
||||
```bash
|
||||
pip install harlequin-mysql
|
||||
```
|
||||
|
||||
If you would like to add the Postgres adapter to an existing Harlequin installation:
|
||||
|
||||
```bash
|
||||
pipx inject harlequin harlequin-mysql
|
||||
```
|
||||
|
||||
### As an Extra
|
||||
Alternatively, you can install Harlequin with the `mysql` extra:
|
||||
|
||||
```bash
|
||||
pip install harlequin[mysql]
|
||||
```
|
||||
|
||||
```bash
|
||||
poetry add harlequin[mysql]
|
||||
```
|
||||
|
||||
```bash
|
||||
pipx install harlequin[mysql]
|
||||
```
|
||||
|
||||
## Usage and Configuration
|
||||
|
||||
You can open Harlequin with the MySQL adapter by selecting it with the `-a` option and passing connection parameters as CLI options:
|
||||
|
||||
```bash
|
||||
harlequin -a mysql -h localhost -p 3306 -U root --password example --database dev
|
||||
```
|
||||
|
||||
The MySQL adapter does not accept a connection string or DSN.
|
||||
|
||||
Many more options are available; to see the full list, run:
|
||||
|
||||
```bash
|
||||
harlequin --help
|
||||
```
|
||||
|
||||
For more information, see the [Harlequin Docs](https://harlequin.sh/docs/mysql/index).
|
9
docker-compose.yml
Normal file
9
docker-compose.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
services:
|
||||
|
||||
db:
|
||||
image: mysql
|
||||
restart: always
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: example
|
||||
ports:
|
||||
- 3306:3306
|
1483
poetry.lock
generated
Normal file
1483
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
67
pyproject.toml
Normal file
67
pyproject.toml
Normal file
|
@ -0,0 +1,67 @@
|
|||
[tool.poetry]
|
||||
name = "harlequin-mysql"
|
||||
version = "1.1.0"
|
||||
description = "A Harlequin adapter for MySQL."
|
||||
authors = ["Ted Conbeer <tconbeer@users.noreply.github.com>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
packages = [
|
||||
{ include = "harlequin_mysql", from = "src" },
|
||||
]
|
||||
|
||||
[tool.poetry.plugins."harlequin.adapter"]
|
||||
mysql = "harlequin_mysql:HarlequinMySQLAdapter"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<3.14"
|
||||
harlequin = ">=1.25.0,<3"
|
||||
mysql-connector-python = "^9.1.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "^0.6"
|
||||
pytest = "^8"
|
||||
mypy = "^1.11"
|
||||
pre-commit = "^3.5.0"
|
||||
importlib_metadata = { version = ">=4.6.0", python = "<3.10.0" }
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py39"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["A", "B", "E", "F", "I"]
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.9"
|
||||
files = [
|
||||
"src/**/*.py",
|
||||
"tests/**/*.py",
|
||||
]
|
||||
mypy_path = "src:stubs"
|
||||
|
||||
show_column_numbers = true
|
||||
|
||||
# show error messages from unrelated files
|
||||
follow_imports = "normal"
|
||||
|
||||
# be strict
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_defs = true
|
||||
check_untyped_defs = true
|
||||
disallow_untyped_decorators = true
|
||||
disallow_incomplete_defs = true
|
||||
disallow_subclassing_any = true
|
||||
strict_optional = true
|
||||
|
||||
warn_return_any = true
|
||||
warn_no_return = true
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
warn_unused_configs = true
|
||||
|
||||
no_implicit_reexport = true
|
||||
strict_equality = true
|
3
src/harlequin_mysql/__init__.py
Normal file
3
src/harlequin_mysql/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from harlequin_mysql.adapter import HarlequinMySQLAdapter
|
||||
|
||||
__all__ = ["HarlequinMySQLAdapter"]
|
428
src/harlequin_mysql/adapter.py
Normal file
428
src/harlequin_mysql/adapter.py
Normal file
|
@ -0,0 +1,428 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from contextlib import suppress
|
||||
from typing import Any, Sequence
|
||||
|
||||
from harlequin import (
|
||||
HarlequinAdapter,
|
||||
HarlequinConnection,
|
||||
HarlequinCursor,
|
||||
)
|
||||
from harlequin.autocomplete.completion import HarlequinCompletion
|
||||
from harlequin.catalog import Catalog, CatalogItem
|
||||
from harlequin.exception import (
|
||||
HarlequinConfigError,
|
||||
HarlequinConnectionError,
|
||||
HarlequinQueryError,
|
||||
)
|
||||
from mysql.connector import FieldType
|
||||
from mysql.connector.cursor import MySQLCursor
|
||||
from mysql.connector.errors import InternalError, PoolError
|
||||
from mysql.connector.pooling import (
|
||||
MySQLConnectionPool,
|
||||
PooledMySQLConnection,
|
||||
)
|
||||
from textual_fastdatatable.backend import AutoBackendType
|
||||
|
||||
from harlequin_mysql.catalog import DatabaseCatalogItem
|
||||
from harlequin_mysql.cli_options import MYSQLADAPTER_OPTIONS
|
||||
from harlequin_mysql.completions import load_completions
|
||||
|
||||
USE_DATABASE_PROG = re.compile(
|
||||
r"\s*use\s+([^\\/?%*:|\"<>.]{1,64})", flags=re.IGNORECASE
|
||||
)
|
||||
QUERY_INTERRUPT_MSG = "1317 (70100): Query execution was interrupted"
|
||||
|
||||
|
||||
class HarlequinMySQLCursor(HarlequinCursor):
|
||||
def __init__(
|
||||
self,
|
||||
cur: MySQLCursor,
|
||||
conn: PooledMySQLConnection,
|
||||
harlequin_conn: HarlequinMySQLConnection,
|
||||
*_: Any,
|
||||
**__: Any,
|
||||
) -> None:
|
||||
self.cur = cur
|
||||
|
||||
# copy description in case the cursor is closed before columns() is called
|
||||
assert cur.description is not None
|
||||
self.description = cur.description.copy()
|
||||
|
||||
self.conn = conn
|
||||
self.harlequin_conn = harlequin_conn
|
||||
self.connection_id = conn._cnx.connection_id
|
||||
self._limit: int | None = None
|
||||
|
||||
def columns(self) -> list[tuple[str, str]]:
|
||||
return [(col[0], self._get_short_type(col[1])) for col in self.description]
|
||||
|
||||
def set_limit(self, limit: int) -> "HarlequinMySQLCursor":
|
||||
self._limit = limit
|
||||
return self
|
||||
|
||||
def fetchall(self) -> AutoBackendType:
|
||||
try:
|
||||
if self._limit is None:
|
||||
results = self.cur.fetchall()
|
||||
else:
|
||||
results = self.cur.fetchmany(self._limit)
|
||||
return results
|
||||
except Exception as e:
|
||||
if str(e) == QUERY_INTERRUPT_MSG:
|
||||
return []
|
||||
else:
|
||||
raise HarlequinQueryError(
|
||||
msg=str(e),
|
||||
title="Harlequin encountered an error while executing your query.",
|
||||
) from e
|
||||
finally:
|
||||
self.conn.consume_results()
|
||||
self.cur.close()
|
||||
self.conn.close()
|
||||
if self.connection_id:
|
||||
self.harlequin_conn._in_use_connections.discard(self.connection_id)
|
||||
|
||||
@staticmethod
|
||||
def _get_short_type(type_id: int) -> str:
|
||||
mapping = {
|
||||
FieldType.BIT: "010",
|
||||
FieldType.BLOB: "0b",
|
||||
FieldType.DATE: "d",
|
||||
FieldType.DATETIME: "dt",
|
||||
FieldType.DECIMAL: "#.#",
|
||||
FieldType.DOUBLE: "#.#",
|
||||
FieldType.ENUM: "enum",
|
||||
FieldType.FLOAT: "#.#",
|
||||
FieldType.GEOMETRY: "▽□",
|
||||
FieldType.INT24: "###",
|
||||
FieldType.JSON: "{}",
|
||||
FieldType.LONG: "##",
|
||||
FieldType.LONGLONG: "##",
|
||||
FieldType.LONG_BLOB: "00b",
|
||||
FieldType.MEDIUM_BLOB: "00b",
|
||||
FieldType.NEWDATE: "d",
|
||||
FieldType.NEWDECIMAL: "#.#",
|
||||
FieldType.NULL: "∅",
|
||||
FieldType.SET: "set",
|
||||
FieldType.SHORT: "#",
|
||||
FieldType.STRING: "s",
|
||||
FieldType.TIME: "t",
|
||||
FieldType.TIMESTAMP: "#ts",
|
||||
FieldType.TINY: "#",
|
||||
FieldType.TINY_BLOB: "b",
|
||||
FieldType.VARCHAR: "s",
|
||||
FieldType.VAR_STRING: "s",
|
||||
FieldType.YEAR: "y",
|
||||
}
|
||||
return mapping.get(type_id, "?")
|
||||
|
||||
|
||||
class HarlequinMySQLConnection(HarlequinConnection):
|
||||
def __init__(
|
||||
self,
|
||||
conn_str: Sequence[str],
|
||||
*_: Any,
|
||||
init_message: str = "",
|
||||
options: dict[str, Any],
|
||||
) -> None:
|
||||
self.init_message = init_message
|
||||
self._in_use_connections: set[int] = set()
|
||||
try:
|
||||
self._pool: MySQLConnectionPool = MySQLConnectionPool(
|
||||
pool_name="harlequin",
|
||||
pool_reset_session=False,
|
||||
autocommit=True,
|
||||
**options,
|
||||
)
|
||||
except Exception as e:
|
||||
raise HarlequinConnectionError(
|
||||
msg=str(e), title="Harlequin could not connect to your database."
|
||||
) from e
|
||||
|
||||
def safe_get_mysql_cursor(
|
||||
self, buffered: bool = False
|
||||
) -> tuple[PooledMySQLConnection | None, MySQLCursor | None]:
|
||||
"""
|
||||
Return None if the connection pool is exhausted, to avoid getting
|
||||
in an unrecoverable state.
|
||||
"""
|
||||
try:
|
||||
conn = self._pool.get_connection()
|
||||
except (InternalError, PoolError):
|
||||
# if we're out of connections, we can't raise a query error,
|
||||
# or we get in a state where we have cursors without fetched
|
||||
# results, which requires a restart of Harlequin. Instead,
|
||||
# just return None and silently fail (there isn't a sensible
|
||||
# way to show an error to the user without aborting processing
|
||||
# all the other cursors).
|
||||
return None, None
|
||||
|
||||
try:
|
||||
cur: MySQLCursor = conn.cursor(buffered=buffered)
|
||||
except InternalError:
|
||||
# cursor has an unread result. Try to consume the results,
|
||||
# and try again.
|
||||
conn.consume_results()
|
||||
cur = conn.cursor(buffered=buffered)
|
||||
|
||||
return conn, cur
|
||||
|
||||
def set_pool_config(self, **config: Any) -> None:
|
||||
"""
|
||||
Updates the config of the MySQL connection pool.
|
||||
"""
|
||||
self._pool.set_config(**config)
|
||||
|
||||
def execute(self, query: str) -> HarlequinCursor | None:
|
||||
retval: HarlequinCursor | None = None
|
||||
|
||||
conn, cur = self.safe_get_mysql_cursor()
|
||||
if conn is None or cur is None:
|
||||
return None
|
||||
else:
|
||||
connection_id = conn._cnx.connection_id
|
||||
if connection_id:
|
||||
self._in_use_connections.add(connection_id)
|
||||
|
||||
try:
|
||||
cur.execute(query)
|
||||
except Exception as e:
|
||||
cur.close()
|
||||
conn.close()
|
||||
if connection_id:
|
||||
self._in_use_connections.discard(connection_id)
|
||||
if str(e) == QUERY_INTERRUPT_MSG:
|
||||
return None
|
||||
else:
|
||||
raise HarlequinQueryError(
|
||||
msg=str(e),
|
||||
title="Harlequin encountered an error while executing your query.",
|
||||
) from e
|
||||
else:
|
||||
if cur.description is not None:
|
||||
retval = HarlequinMySQLCursor(cur, conn=conn, harlequin_conn=self)
|
||||
else:
|
||||
cur.close()
|
||||
conn.close()
|
||||
if connection_id:
|
||||
self._in_use_connections.discard(connection_id)
|
||||
|
||||
# this is a hack to update all connections in the pool if the user
|
||||
# changes the database for the active connection.
|
||||
# it is impossible to check the database or other config
|
||||
# of a connection with an open cursor, and we can't use a dedicated
|
||||
# connection for user queries, since mysql only supports a single
|
||||
# (unfetched) cursor per connection.
|
||||
if match := USE_DATABASE_PROG.match(query):
|
||||
new_db = match.group(1)
|
||||
self.set_pool_config(database=new_db)
|
||||
return retval
|
||||
|
||||
def cancel(self) -> None:
|
||||
# get a new cursor to execute the KILL statements
|
||||
conn, cur = self.safe_get_mysql_cursor()
|
||||
if conn is None or cur is None:
|
||||
return None
|
||||
|
||||
# loop through in-use connections and kill each of them
|
||||
for connection_id in self._in_use_connections:
|
||||
try:
|
||||
cur.execute("KILL QUERY %s", (connection_id,))
|
||||
except BaseException:
|
||||
continue
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
self._in_use_connections = set()
|
||||
|
||||
def close(self) -> None:
|
||||
with suppress(PoolError):
|
||||
self._pool._remove_connections()
|
||||
|
||||
def get_catalog(self) -> Catalog:
|
||||
databases = self._get_databases()
|
||||
db_items: list[CatalogItem] = [
|
||||
DatabaseCatalogItem.from_label(label=db, connection=self)
|
||||
for (db,) in databases
|
||||
]
|
||||
return Catalog(items=db_items)
|
||||
|
||||
def get_completions(self) -> list[HarlequinCompletion]:
|
||||
return load_completions()
|
||||
|
||||
def _get_databases(self) -> list[tuple[str]]:
|
||||
conn, cur = self.safe_get_mysql_cursor(buffered=True)
|
||||
if conn is None or cur is None:
|
||||
raise HarlequinConnectionError(
|
||||
title="Connection pool exhausted",
|
||||
msg=(
|
||||
"Connection pool exhausted. Try restarting Harlequin "
|
||||
"with a larger pool or running fewer queries at once."
|
||||
),
|
||||
)
|
||||
cur.execute(
|
||||
"""
|
||||
show databases
|
||||
where `Database` not in (
|
||||
'sys', 'information_schema', 'performance_schema', 'mysql'
|
||||
)
|
||||
"""
|
||||
)
|
||||
results: list[tuple[str]] = cur.fetchall() # type: ignore
|
||||
cur.close()
|
||||
conn.close()
|
||||
return results
|
||||
|
||||
def _get_relations(self, db_name: str) -> list[tuple[str, str]]:
|
||||
conn, cur = self.safe_get_mysql_cursor(buffered=True)
|
||||
if conn is None or cur is None:
|
||||
raise HarlequinConnectionError(
|
||||
title="Connection pool exhausted",
|
||||
msg=(
|
||||
"Connection pool exhausted. Try restarting Harlequin "
|
||||
"with a larger pool or running fewer queries at once."
|
||||
),
|
||||
)
|
||||
cur.execute(
|
||||
f"""
|
||||
select
|
||||
table_name,
|
||||
table_type
|
||||
from information_schema.tables
|
||||
where table_schema = '{db_name}'
|
||||
and table_type != 'SYSTEM VIEW'
|
||||
order by table_name asc
|
||||
;"""
|
||||
)
|
||||
results: list[tuple[str, str]] = cur.fetchall() # type: ignore
|
||||
cur.close()
|
||||
conn.close()
|
||||
return results
|
||||
|
||||
def _get_columns(self, db_name: str, rel_name: str) -> list[tuple[str, str]]:
|
||||
conn, cur = self.safe_get_mysql_cursor(buffered=True)
|
||||
if conn is None or cur is None:
|
||||
raise HarlequinConnectionError(
|
||||
title="Connection pool exhausted",
|
||||
msg=(
|
||||
"Connection pool exhausted. Try restarting Harlequin "
|
||||
"with a larger pool or running fewer queries at once."
|
||||
),
|
||||
)
|
||||
cur.execute(
|
||||
f"""
|
||||
select column_name, data_type
|
||||
from information_schema.columns
|
||||
where
|
||||
table_schema = '{db_name}'
|
||||
and table_name = '{rel_name}'
|
||||
and extra not like '%INVISIBLE%'
|
||||
order by ordinal_position asc
|
||||
;"""
|
||||
)
|
||||
results: list[tuple[str, str]] = cur.fetchall() # type: ignore
|
||||
cur.close()
|
||||
conn.close()
|
||||
return results
|
||||
|
||||
@staticmethod
|
||||
def _short_column_type(info_schema_type: str) -> str:
|
||||
mapping = {
|
||||
"bigint": "###",
|
||||
"binary": "010",
|
||||
"blob": "0b",
|
||||
"char": "c",
|
||||
"datetime": "dt",
|
||||
"decimal": "#.#",
|
||||
"double": "#.#",
|
||||
"enum": "enum",
|
||||
"float": "#.#",
|
||||
"int": "##",
|
||||
"json": "{}",
|
||||
"longblob": "00b",
|
||||
"longtext": "ss",
|
||||
"mediumblob": "00b",
|
||||
"mediumint": "##",
|
||||
"mediumtext": "s",
|
||||
"set": "set",
|
||||
"smallint": "#",
|
||||
"text": "s",
|
||||
"time": "t",
|
||||
"timestamp": "ts",
|
||||
"tinyint": "#",
|
||||
"varbinary": "010",
|
||||
"varchar": "s",
|
||||
}
|
||||
return mapping.get(info_schema_type, "?")
|
||||
|
||||
|
||||
class HarlequinMySQLAdapter(HarlequinAdapter):
|
||||
ADAPTER_OPTIONS = MYSQLADAPTER_OPTIONS
|
||||
IMPLEMENTS_CANCEL = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
conn_str: Sequence[str],
|
||||
host: str | None = None,
|
||||
port: str | int | None = 3306,
|
||||
unix_socket: str | None = None,
|
||||
database: str | None = None,
|
||||
user: str | None = None,
|
||||
password: str | None = None,
|
||||
password2: str | None = None,
|
||||
password3: str | None = None,
|
||||
connection_timeout: str | int | None = None,
|
||||
ssl_ca: str | None = None,
|
||||
ssl_cert: str | None = None,
|
||||
ssl_disabled: str | bool | None = False,
|
||||
ssl_key: str | None = None,
|
||||
openid_token_file: str | None = None,
|
||||
pool_size: str | int | None = 5,
|
||||
**_: Any,
|
||||
) -> None:
|
||||
if conn_str:
|
||||
raise HarlequinConnectionError(
|
||||
f"Cannot provide a DSN to the MySQL adapter. Got:\n{conn_str}"
|
||||
)
|
||||
try:
|
||||
self.options = {
|
||||
"host": host,
|
||||
"port": int(port) if port is not None else 3306,
|
||||
"unix_socket": unix_socket,
|
||||
"database": database,
|
||||
"user": user,
|
||||
"password": password,
|
||||
"password2": password2,
|
||||
"password3": password3,
|
||||
"connection_timeout": int(connection_timeout)
|
||||
if connection_timeout is not None
|
||||
else None,
|
||||
"ssl_ca": ssl_ca,
|
||||
"ssl_cert": ssl_cert,
|
||||
"ssl_disabled": ssl_disabled if ssl_disabled is not None else False,
|
||||
"ssl_key": ssl_key,
|
||||
"openid_token_file": openid_token_file,
|
||||
"pool_size": int(pool_size) if pool_size is not None else 5,
|
||||
}
|
||||
except (ValueError, TypeError) as e:
|
||||
raise HarlequinConfigError(
|
||||
msg=f"MySQL adapter received bad config value: {e}",
|
||||
title="Harlequin could not initialize the selected adapter.",
|
||||
) from e
|
||||
|
||||
@property
|
||||
def connection_id(self) -> str | None:
|
||||
host = self.options.get("host", "") or ""
|
||||
sock = self.options.get("unix_socket", "") or ""
|
||||
host = host if host or sock else "127.0.0.1"
|
||||
|
||||
port = self.options.get("port", 3306)
|
||||
database = self.options.get("database", "") or ""
|
||||
|
||||
return f"{host}{sock}:{port}/{database}"
|
||||
|
||||
def connect(self) -> HarlequinMySQLConnection:
|
||||
conn = HarlequinMySQLConnection(conn_str=tuple(), options=self.options)
|
||||
return conn
|
154
src/harlequin_mysql/catalog.py
Normal file
154
src/harlequin_mysql/catalog.py
Normal file
|
@ -0,0 +1,154 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from harlequin.catalog import InteractiveCatalogItem
|
||||
|
||||
from harlequin_mysql.interactions import (
|
||||
execute_drop_database_statement,
|
||||
execute_drop_table_statement,
|
||||
execute_drop_view_statement,
|
||||
execute_use_statement,
|
||||
insert_columns_at_cursor,
|
||||
show_select_star,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from harlequin_mysql.adapter import HarlequinMySQLConnection
|
||||
|
||||
|
||||
@dataclass
|
||||
class ColumnCatalogItem(InteractiveCatalogItem["HarlequinMySQLConnection"]):
|
||||
parent: "RelationCatalogItem" | None = None
|
||||
|
||||
@classmethod
|
||||
def from_parent(
|
||||
cls,
|
||||
parent: "RelationCatalogItem",
|
||||
label: str,
|
||||
type_label: str,
|
||||
) -> "ColumnCatalogItem":
|
||||
column_qualified_identifier = f"{parent.qualified_identifier}.`{label}`"
|
||||
column_query_name = f"`{label}`"
|
||||
return cls(
|
||||
qualified_identifier=column_qualified_identifier,
|
||||
query_name=column_query_name,
|
||||
label=label,
|
||||
type_label=type_label,
|
||||
connection=parent.connection,
|
||||
parent=parent,
|
||||
loaded=True,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class RelationCatalogItem(InteractiveCatalogItem["HarlequinMySQLConnection"]):
|
||||
INTERACTIONS = [
|
||||
("Insert Columns at Cursor", insert_columns_at_cursor),
|
||||
("Preview Data", show_select_star),
|
||||
]
|
||||
parent: "DatabaseCatalogItem" | None = None
|
||||
|
||||
def fetch_children(self) -> list[ColumnCatalogItem]:
|
||||
if self.parent is None or self.connection is None:
|
||||
return []
|
||||
result = self.connection._get_columns(self.parent.label, self.label)
|
||||
return [
|
||||
ColumnCatalogItem.from_parent(
|
||||
parent=self,
|
||||
label=column_name,
|
||||
type_label=self.connection._short_column_type(column_type),
|
||||
)
|
||||
for column_name, column_type in result
|
||||
]
|
||||
|
||||
|
||||
class ViewCatalogItem(RelationCatalogItem):
|
||||
INTERACTIONS = RelationCatalogItem.INTERACTIONS + [
|
||||
("Drop View", execute_drop_view_statement),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def from_parent(
|
||||
cls,
|
||||
parent: "DatabaseCatalogItem",
|
||||
label: str,
|
||||
) -> "ViewCatalogItem":
|
||||
relation_query_name = f"`{parent.label}`.`{label}`"
|
||||
relation_qualified_identifier = f"{parent.qualified_identifier}.`{label}`"
|
||||
return cls(
|
||||
qualified_identifier=relation_qualified_identifier,
|
||||
query_name=relation_query_name,
|
||||
label=label,
|
||||
type_label="v",
|
||||
connection=parent.connection,
|
||||
parent=parent,
|
||||
)
|
||||
|
||||
|
||||
class TableCatalogItem(RelationCatalogItem):
|
||||
INTERACTIONS = RelationCatalogItem.INTERACTIONS + [
|
||||
("Drop Table", execute_drop_table_statement),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def from_parent(
|
||||
cls,
|
||||
parent: "DatabaseCatalogItem",
|
||||
label: str,
|
||||
) -> "TableCatalogItem":
|
||||
relation_query_name = f"`{parent.label}`.`{label}`"
|
||||
relation_qualified_identifier = f"{parent.qualified_identifier}.`{label}`"
|
||||
return cls(
|
||||
qualified_identifier=relation_qualified_identifier,
|
||||
query_name=relation_query_name,
|
||||
label=label,
|
||||
type_label="t",
|
||||
connection=parent.connection,
|
||||
parent=parent,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class DatabaseCatalogItem(InteractiveCatalogItem["HarlequinMySQLConnection"]):
|
||||
INTERACTIONS = [
|
||||
("Set Editor Context (USE)", execute_use_statement),
|
||||
("Drop Database", execute_drop_database_statement),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def from_label(
|
||||
cls, label: str, connection: "HarlequinMySQLConnection"
|
||||
) -> "DatabaseCatalogItem":
|
||||
database_identifier = f"`{label}`"
|
||||
return cls(
|
||||
qualified_identifier=database_identifier,
|
||||
query_name=database_identifier,
|
||||
label=label,
|
||||
type_label="db",
|
||||
connection=connection,
|
||||
)
|
||||
|
||||
def fetch_children(self) -> list[RelationCatalogItem]:
|
||||
if self.connection is None:
|
||||
return []
|
||||
children: list[RelationCatalogItem] = []
|
||||
result = self.connection._get_relations(self.label)
|
||||
for table_label, table_type in result:
|
||||
if table_type == "VIEW":
|
||||
children.append(
|
||||
ViewCatalogItem.from_parent(
|
||||
parent=self,
|
||||
label=table_label,
|
||||
)
|
||||
)
|
||||
else:
|
||||
children.append(
|
||||
TableCatalogItem.from_parent(
|
||||
parent=self,
|
||||
label=table_label,
|
||||
)
|
||||
)
|
||||
|
||||
return children
|
151
src/harlequin_mysql/cli_options.py
Normal file
151
src/harlequin_mysql/cli_options.py
Normal file
|
@ -0,0 +1,151 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from harlequin.options import (
|
||||
FlagOption,
|
||||
PathOption,
|
||||
TextOption,
|
||||
)
|
||||
|
||||
|
||||
def _int_validator(s: str | None) -> tuple[bool, str]:
|
||||
if s is None:
|
||||
return True, ""
|
||||
try:
|
||||
_ = int(s)
|
||||
except ValueError:
|
||||
return False, f"Cannot convert {s} to an int!"
|
||||
else:
|
||||
return True, ""
|
||||
|
||||
|
||||
host = TextOption(
|
||||
name="host",
|
||||
description=("The host name or IP address of the MySQL server."),
|
||||
short_decls=["-h"],
|
||||
default="localhost",
|
||||
)
|
||||
|
||||
|
||||
port = TextOption(
|
||||
name="port",
|
||||
description=("The TCP/IP port of the MySQL server. Must be an integer."),
|
||||
short_decls=["-p"],
|
||||
default="3306",
|
||||
validator=_int_validator,
|
||||
)
|
||||
|
||||
|
||||
unix_socket = TextOption(
|
||||
name="unix_socket",
|
||||
description=("The location of the Unix socket file."),
|
||||
)
|
||||
|
||||
|
||||
database = TextOption(
|
||||
name="database",
|
||||
description=("The database name to use when connecting with the MySQL server."),
|
||||
short_decls=["-d", "-db"],
|
||||
default="postgres",
|
||||
)
|
||||
|
||||
|
||||
user = TextOption(
|
||||
name="user",
|
||||
description=("The user name used to authenticate with the MySQL server."),
|
||||
short_decls=["-u", "--username", "-U"],
|
||||
)
|
||||
|
||||
|
||||
password = TextOption(
|
||||
name="password",
|
||||
description=("The password to authenticate the user with the MySQL server."),
|
||||
short_decls=["--password1"],
|
||||
)
|
||||
|
||||
|
||||
password2 = TextOption(
|
||||
name="password2",
|
||||
description=("For Multi-Factor Authentication (MFA); Added in 8.0.28."),
|
||||
)
|
||||
|
||||
|
||||
password3 = TextOption(
|
||||
name="password3",
|
||||
description=("For Multi-Factor Authentication (MFA); Added in 8.0.28."),
|
||||
)
|
||||
|
||||
|
||||
connect_timeout = TextOption(
|
||||
name="connection_timeout",
|
||||
description="Timeout for the TCP and Unix socket connections. Must be an integer.",
|
||||
short_decls=["--connect_timeout"],
|
||||
validator=_int_validator,
|
||||
)
|
||||
|
||||
|
||||
ssl_ca = PathOption(
|
||||
name="ssl-ca",
|
||||
description="File containing the SSL certificate authority.",
|
||||
exists=True,
|
||||
dir_okay=False,
|
||||
)
|
||||
|
||||
ssl_cert = PathOption(
|
||||
name="ssl-cert",
|
||||
description="File containing the SSL certificate file.",
|
||||
exists=True,
|
||||
dir_okay=False,
|
||||
short_decls=["--sslcert"],
|
||||
)
|
||||
|
||||
ssl_disabled = FlagOption(
|
||||
name="ssl-disabled",
|
||||
description="True disables SSL/TLS usage.",
|
||||
)
|
||||
|
||||
ssl_key = PathOption(
|
||||
name="ssl-key",
|
||||
description="File containing the SSL key.",
|
||||
exists=True,
|
||||
dir_okay=False,
|
||||
short_decls=["--sslkey"],
|
||||
)
|
||||
|
||||
openid_token_file = PathOption(
|
||||
name="openid-token-file",
|
||||
description="File containing the OpenID Token.",
|
||||
exists=True,
|
||||
dir_okay=False,
|
||||
short_decls=["--oid"],
|
||||
)
|
||||
|
||||
pool_size = TextOption(
|
||||
name="pool-size",
|
||||
description=(
|
||||
"The number of concurrent connections maintained by Harlequin. MySQL "
|
||||
"only allows one cursor per connection, so this sets the number of queries "
|
||||
"that can be executed at once in Harlequin."
|
||||
),
|
||||
short_decls=["-n"],
|
||||
default="5",
|
||||
validator=_int_validator,
|
||||
)
|
||||
|
||||
|
||||
MYSQLADAPTER_OPTIONS = [
|
||||
host,
|
||||
port,
|
||||
unix_socket,
|
||||
database,
|
||||
user,
|
||||
password,
|
||||
password2,
|
||||
password3,
|
||||
connect_timeout,
|
||||
ssl_ca,
|
||||
ssl_cert,
|
||||
ssl_disabled,
|
||||
ssl_key,
|
||||
openid_token_file,
|
||||
pool_size,
|
||||
]
|
48
src/harlequin_mysql/completions.py
Normal file
48
src/harlequin_mysql/completions.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from harlequin import HarlequinCompletion
|
||||
|
||||
WORD = re.compile(r"\w+")
|
||||
|
||||
|
||||
def load_completions() -> list[HarlequinCompletion]:
|
||||
completions: list[HarlequinCompletion] = []
|
||||
|
||||
keywords_path = Path(__file__).parent / "keywords.csv"
|
||||
with keywords_path.open("r") as f:
|
||||
reader = csv.reader(f, dialect="unix")
|
||||
for name, reserved, removed in reader:
|
||||
if removed == "False":
|
||||
completions.append(
|
||||
HarlequinCompletion(
|
||||
label=name.lower(),
|
||||
type_label="kw",
|
||||
value=name.lower(),
|
||||
priority=100 if reserved else 1000,
|
||||
context=None,
|
||||
)
|
||||
)
|
||||
|
||||
functions_path = Path(__file__).parent / "functions.tsv"
|
||||
with functions_path.open("r") as f:
|
||||
reader = csv.reader(f, dialect="unix", delimiter="\t")
|
||||
for name, _, _, deprecated in reader:
|
||||
if deprecated:
|
||||
continue
|
||||
for alias in name.split(", "):
|
||||
if WORD.match(alias):
|
||||
completions.append(
|
||||
HarlequinCompletion(
|
||||
label=alias.split("...")[0].split("(")[0].lower(),
|
||||
type_label="fn",
|
||||
value=alias.split("...")[0].split("(")[0].lower(),
|
||||
priority=1000,
|
||||
context=None,
|
||||
)
|
||||
)
|
||||
|
||||
return completions
|
441
src/harlequin_mysql/functions.tsv
Normal file
441
src/harlequin_mysql/functions.tsv
Normal file
|
@ -0,0 +1,441 @@
|
|||
Name Description Introduced Deprecated
|
||||
ABS() Return the absolute value
|
||||
ACOS() Return the arc cosine
|
||||
ADDDATE() Add time values (intervals) to a date value
|
||||
ADDTIME() Add time
|
||||
AES_DECRYPT() Decrypt using AES
|
||||
AES_ENCRYPT() Encrypt using AES
|
||||
AND, && Logical AND
|
||||
ANY_VALUE() Suppress ONLY_FULL_GROUP_BY value rejection
|
||||
ASCII() Return numeric value of left-most character
|
||||
ASIN() Return the arc sine
|
||||
asynchronous_connection_failover_add_managed() Add group member source server configuration information to a replication channel source list 8.0.23
|
||||
asynchronous_connection_failover_add_source() Add source server configuration information server to a replication channel source list 8.0.22
|
||||
asynchronous_connection_failover_delete_managed() Remove a managed group from a replication channel source list 8.0.23
|
||||
asynchronous_connection_failover_delete_source() Remove a source server from a replication channel source list 8.0.22
|
||||
asynchronous_connection_failover_reset() Remove all settings relating to group replication asynchronous failover 8.0.27
|
||||
ATAN() Return the arc tangent
|
||||
ATAN2(), ATAN() Return the arc tangent of the two arguments
|
||||
AVG() Return the average value of the argument
|
||||
BENCHMARK() Repeatedly execute an expression
|
||||
BETWEEN ... AND ... Whether a value is within a range of values
|
||||
BIN() Return a string containing binary representation of a number
|
||||
BIN_TO_UUID() Convert binary UUID to string
|
||||
BINARY Cast a string to a binary string 8.0.27
|
||||
BIT_AND() Return bitwise AND
|
||||
BIT_COUNT() Return the number of bits that are set
|
||||
BIT_LENGTH() Return length of argument in bits
|
||||
BIT_OR() Return bitwise OR
|
||||
BIT_XOR() Return bitwise XOR
|
||||
CAN_ACCESS_COLUMN() Internal use only
|
||||
CAN_ACCESS_DATABASE() Internal use only
|
||||
CAN_ACCESS_TABLE() Internal use only
|
||||
CAN_ACCESS_USER() Internal use only 8.0.22
|
||||
CAN_ACCESS_VIEW() Internal use only
|
||||
CASE Case operator
|
||||
CAST() Cast a value as a certain type
|
||||
CEIL() Return the smallest integer value not less than the argument
|
||||
CEILING() Return the smallest integer value not less than the argument
|
||||
CHAR() Return the character for each integer passed
|
||||
CHAR_LENGTH() Return number of characters in argument
|
||||
CHARACTER_LENGTH() Synonym for CHAR_LENGTH()
|
||||
CHARSET() Return the character set of the argument
|
||||
COALESCE() Return the first non-NULL argument
|
||||
COERCIBILITY() Return the collation coercibility value of the string argument
|
||||
COLLATION() Return the collation of the string argument
|
||||
COMPRESS() Return result as a binary string
|
||||
CONCAT() Return concatenated string
|
||||
CONCAT_WS() Return concatenate with separator
|
||||
CONNECTION_ID() Return the connection ID (thread ID) for the connection
|
||||
CONV() Convert numbers between different number bases
|
||||
CONVERT() Cast a value as a certain type
|
||||
CONVERT_TZ() Convert from one time zone to another
|
||||
COS() Return the cosine
|
||||
COT() Return the cotangent
|
||||
COUNT() Return a count of the number of rows returned
|
||||
COUNT(DISTINCT) Return the count of a number of different values
|
||||
CRC32() Compute a cyclic redundancy check value
|
||||
CUME_DIST() Cumulative distribution value
|
||||
CURDATE() Return the current date
|
||||
CURRENT_DATE(), CURRENT_DATE Synonyms for CURDATE()
|
||||
CURRENT_ROLE() Return the current active roles
|
||||
CURRENT_TIME(), CURRENT_TIME Synonyms for CURTIME()
|
||||
CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP Synonyms for NOW()
|
||||
CURRENT_USER(), CURRENT_USER The authenticated user name and host name
|
||||
CURTIME() Return the current time
|
||||
DATABASE() Return the default (current) database name
|
||||
DATE() Extract the date part of a date or datetime expression
|
||||
DATE_ADD() Add time values (intervals) to a date value
|
||||
DATE_FORMAT() Format date as specified
|
||||
DATE_SUB() Subtract a time value (interval) from a date
|
||||
DATEDIFF() Subtract two dates
|
||||
DAY() Synonym for DAYOFMONTH()
|
||||
DAYNAME() Return the name of the weekday
|
||||
DAYOFMONTH() Return the day of the month (0-31)
|
||||
DAYOFWEEK() Return the weekday index of the argument
|
||||
DAYOFYEAR() Return the day of the year (1-366)
|
||||
DEFAULT() Return the default value for a table column
|
||||
DEGREES() Convert radians to degrees
|
||||
DENSE_RANK() Rank of current row within its partition, without gaps
|
||||
DIV Integer division
|
||||
ELT() Return string at index number
|
||||
EXP() Raise to the power of
|
||||
EXPORT_SET() Return a string such that for every bit set in the value bits, you get an on string and for every unset bit, you get an off string
|
||||
EXTRACT() Extract part of a date
|
||||
ExtractValue() Extract a value from an XML string using XPath notation
|
||||
FIELD() Index (position) of first argument in subsequent arguments
|
||||
FIND_IN_SET() Index (position) of first argument within second argument
|
||||
FIRST_VALUE() Value of argument from first row of window frame
|
||||
FLOOR() Return the largest integer value not greater than the argument
|
||||
FORMAT() Return a number formatted to specified number of decimal places
|
||||
FORMAT_BYTES() Convert byte count to value with units 8.0.16
|
||||
FORMAT_PICO_TIME() Convert time in picoseconds to value with units 8.0.16
|
||||
FOUND_ROWS() For a SELECT with a LIMIT clause, the number of rows that would be returned were there no LIMIT clause
|
||||
FROM_BASE64() Decode base64 encoded string and return result
|
||||
FROM_DAYS() Convert a day number to a date
|
||||
FROM_UNIXTIME() Format Unix timestamp as a date
|
||||
GeomCollection() Construct geometry collection from geometries
|
||||
GeometryCollection() Construct geometry collection from geometries
|
||||
GET_DD_COLUMN_PRIVILEGES() Internal use only
|
||||
GET_DD_CREATE_OPTIONS() Internal use only
|
||||
GET_DD_INDEX_SUB_PART_LENGTH() Internal use only
|
||||
GET_FORMAT() Return a date format string
|
||||
GET_LOCK() Get a named lock
|
||||
GREATEST() Return the largest argument
|
||||
GROUP_CONCAT() Return a concatenated string
|
||||
group_replication_disable_member_action() Disable member action for event specified 8.0.26
|
||||
group_replication_enable_member_action() Enable member action for event specified 8.0.26
|
||||
group_replication_get_communication_protocol() Get version of group replication communication protocol currently in use 8.0.16
|
||||
group_replication_get_write_concurrency() Get maximum number of consensus instances currently set for group 8.0.13
|
||||
group_replication_reset_member_actions() Reset all member actions to defaults and configuration version number to 1 8.0.26
|
||||
group_replication_set_as_primary() Make a specific group member the primary 8.0.29
|
||||
group_replication_set_communication_protocol() Set version for group replication communication protocol to use 8.0.16
|
||||
group_replication_set_write_concurrency() Set maximum number of consensus instances that can be executed in parallel 8.0.13
|
||||
group_replication_switch_to_multi_primary_mode() Changes the mode of a group running in single-primary mode to multi-primary mode 8.0.13
|
||||
group_replication_switch_to_single_primary_mode() Changes the mode of a group running in multi-primary mode to single-primary mode 8.0.13
|
||||
GROUPING() Distinguish super-aggregate ROLLUP rows from regular rows
|
||||
GTID_SUBSET() Return true if all GTIDs in subset are also in set; otherwise false.
|
||||
GTID_SUBTRACT() Return all GTIDs in set that are not in subset.
|
||||
HEX() Hexadecimal representation of decimal or string value
|
||||
HOUR() Extract the hour
|
||||
ICU_VERSION() ICU library version
|
||||
IF() If/else construct
|
||||
IFNULL() Null if/else construct
|
||||
IN() Whether a value is within a set of values
|
||||
INET_ATON() Return the numeric value of an IP address
|
||||
INET_NTOA() Return the IP address from a numeric value
|
||||
INET6_ATON() Return the numeric value of an IPv6 address
|
||||
INET6_NTOA() Return the IPv6 address from a numeric value
|
||||
INSERT() Insert substring at specified position up to specified number of characters
|
||||
INSTR() Return the index of the first occurrence of substring
|
||||
INTERNAL_AUTO_INCREMENT() Internal use only
|
||||
INTERNAL_AVG_ROW_LENGTH() Internal use only
|
||||
INTERNAL_CHECK_TIME() Internal use only
|
||||
INTERNAL_CHECKSUM() Internal use only
|
||||
INTERNAL_DATA_FREE() Internal use only
|
||||
INTERNAL_DATA_LENGTH() Internal use only
|
||||
INTERNAL_DD_CHAR_LENGTH() Internal use only
|
||||
INTERNAL_GET_COMMENT_OR_ERROR() Internal use only
|
||||
INTERNAL_GET_ENABLED_ROLE_JSON() Internal use only 8.0.19
|
||||
INTERNAL_GET_HOSTNAME() Internal use only 8.0.19
|
||||
INTERNAL_GET_USERNAME() Internal use only 8.0.19
|
||||
INTERNAL_GET_VIEW_WARNING_OR_ERROR() Internal use only
|
||||
INTERNAL_INDEX_COLUMN_CARDINALITY() Internal use only
|
||||
INTERNAL_INDEX_LENGTH() Internal use only
|
||||
INTERNAL_IS_ENABLED_ROLE() Internal use only 8.0.19
|
||||
INTERNAL_IS_MANDATORY_ROLE() Internal use only 8.0.19
|
||||
INTERNAL_KEYS_DISABLED() Internal use only
|
||||
INTERNAL_MAX_DATA_LENGTH() Internal use only
|
||||
INTERNAL_TABLE_ROWS() Internal use only
|
||||
INTERNAL_UPDATE_TIME() Internal use only
|
||||
INTERVAL() Return the index of the argument that is less than the first argument
|
||||
IS Test a value against a boolean
|
||||
IS_FREE_LOCK() Whether the named lock is free
|
||||
IS_IPV4() Whether argument is an IPv4 address
|
||||
IS_IPV4_COMPAT() Whether argument is an IPv4-compatible address
|
||||
IS_IPV4_MAPPED() Whether argument is an IPv4-mapped address
|
||||
IS_IPV6() Whether argument is an IPv6 address
|
||||
IS NOT Test a value against a boolean
|
||||
IS NOT NULL NOT NULL value test
|
||||
IS NULL NULL value test
|
||||
IS_USED_LOCK() Whether the named lock is in use; return connection identifier if true
|
||||
IS_UUID() Whether argument is a valid UUID
|
||||
ISNULL() Test whether the argument is NULL
|
||||
JSON_ARRAY() Create JSON array
|
||||
JSON_ARRAY_APPEND() Append data to JSON document
|
||||
JSON_ARRAY_INSERT() Insert into JSON array
|
||||
JSON_ARRAYAGG() Return result set as a single JSON array
|
||||
JSON_CONTAINS() Whether JSON document contains specific object at path
|
||||
JSON_CONTAINS_PATH() Whether JSON document contains any data at path
|
||||
JSON_DEPTH() Maximum depth of JSON document
|
||||
JSON_EXTRACT() Return data from JSON document
|
||||
JSON_INSERT() Insert data into JSON document
|
||||
JSON_KEYS() Array of keys from JSON document
|
||||
JSON_LENGTH() Number of elements in JSON document
|
||||
JSON_MERGE() Merge JSON documents, preserving duplicate keys. Deprecated synonym for JSON_MERGE_PRESERVE() Yes
|
||||
JSON_MERGE_PATCH() Merge JSON documents, replacing values of duplicate keys
|
||||
JSON_MERGE_PRESERVE() Merge JSON documents, preserving duplicate keys
|
||||
JSON_OBJECT() Create JSON object
|
||||
JSON_OBJECTAGG() Return result set as a single JSON object
|
||||
JSON_OVERLAPS() Compares two JSON documents, returns TRUE (1) if these have any key-value pairs or array elements in common, otherwise FALSE (0) 8.0.17
|
||||
JSON_PRETTY() Print a JSON document in human-readable format
|
||||
JSON_QUOTE() Quote JSON document
|
||||
JSON_REMOVE() Remove data from JSON document
|
||||
JSON_REPLACE() Replace values in JSON document
|
||||
JSON_SCHEMA_VALID() Validate JSON document against JSON schema; returns TRUE/1 if document validates against schema, or FALSE/0 if it does not 8.0.17
|
||||
JSON_SCHEMA_VALIDATION_REPORT() Validate JSON document against JSON schema; returns report in JSON format on outcome on validation including success or failure and reasons for failure 8.0.17
|
||||
JSON_SEARCH() Path to value within JSON document
|
||||
JSON_SET() Insert data into JSON document
|
||||
JSON_STORAGE_FREE() Freed space within binary representation of JSON column value following partial update
|
||||
JSON_STORAGE_SIZE() Space used for storage of binary representation of a JSON document
|
||||
JSON_TABLE() Return data from a JSON expression as a relational table
|
||||
JSON_TYPE() Type of JSON value
|
||||
JSON_UNQUOTE() Unquote JSON value
|
||||
JSON_VALID() Whether JSON value is valid
|
||||
JSON_VALUE() Extract value from JSON document at location pointed to by path provided; return this value as VARCHAR(512) or specified type 8.0.21
|
||||
LAG() Value of argument from row lagging current row within partition
|
||||
LAST_DAY Return the last day of the month for the argument
|
||||
LAST_INSERT_ID() Value of the AUTOINCREMENT column for the last INSERT
|
||||
LAST_VALUE() Value of argument from last row of window frame
|
||||
LCASE() Synonym for LOWER()
|
||||
LEAD() Value of argument from row leading current row within partition
|
||||
LEAST() Return the smallest argument
|
||||
LEFT() Return the leftmost number of characters as specified
|
||||
LENGTH() Return the length of a string in bytes
|
||||
LIKE Simple pattern matching
|
||||
LineString() Construct LineString from Point values
|
||||
LN() Return the natural logarithm of the argument
|
||||
LOAD_FILE() Load the named file
|
||||
LOCALTIME(), LOCALTIME Synonym for NOW()
|
||||
LOCALTIMESTAMP, LOCALTIMESTAMP() Synonym for NOW()
|
||||
LOCATE() Return the position of the first occurrence of substring
|
||||
LOG() Return the natural logarithm of the first argument
|
||||
LOG10() Return the base-10 logarithm of the argument
|
||||
LOG2() Return the base-2 logarithm of the argument
|
||||
LOWER() Return the argument in lowercase
|
||||
LPAD() Return the string argument, left-padded with the specified string
|
||||
LTRIM() Remove leading spaces
|
||||
MAKE_SET() Return a set of comma-separated strings that have the corresponding bit in bits set
|
||||
MAKEDATE() Create a date from the year and day of year
|
||||
MAKETIME() Create time from hour, minute, second
|
||||
MASTER_POS_WAIT() Block until the replica has read and applied all updates up to the specified position 8.0.26
|
||||
MATCH() Perform full-text search
|
||||
MAX() Return the maximum value
|
||||
MBRContains() Whether MBR of one geometry contains MBR of another
|
||||
MBRCoveredBy() Whether one MBR is covered by another
|
||||
MBRCovers() Whether one MBR covers another
|
||||
MBRDisjoint() Whether MBRs of two geometries are disjoint
|
||||
MBREquals() Whether MBRs of two geometries are equal
|
||||
MBRIntersects() Whether MBRs of two geometries intersect
|
||||
MBROverlaps() Whether MBRs of two geometries overlap
|
||||
MBRTouches() Whether MBRs of two geometries touch
|
||||
MBRWithin() Whether MBR of one geometry is within MBR of another
|
||||
MD5() Calculate MD5 checksum
|
||||
MEMBER OF() Returns true (1) if first operand matches any element of JSON array passed as second operand, otherwise returns false (0) 8.0.17
|
||||
MICROSECOND() Return the microseconds from argument
|
||||
MID() Return a substring starting from the specified position
|
||||
MIN() Return the minimum value
|
||||
MINUTE() Return the minute from the argument
|
||||
MOD() Return the remainder
|
||||
MONTH() Return the month from the date passed
|
||||
MONTHNAME() Return the name of the month
|
||||
MultiLineString() Contruct MultiLineString from LineString values
|
||||
MultiPoint() Construct MultiPoint from Point values
|
||||
MultiPolygon() Construct MultiPolygon from Polygon values
|
||||
NAME_CONST() Cause the column to have the given name
|
||||
NOT, ! Negates value
|
||||
NOT BETWEEN ... AND ... Whether a value is not within a range of values
|
||||
NOT IN() Whether a value is not within a set of values
|
||||
NOT LIKE Negation of simple pattern matching
|
||||
NOT REGEXP Negation of REGEXP
|
||||
NOW() Return the current date and time
|
||||
NTH_VALUE() Value of argument from N-th row of window frame
|
||||
NTILE() Bucket number of current row within its partition.
|
||||
NULLIF() Return NULL if expr1 = expr2
|
||||
OCT() Return a string containing octal representation of a number
|
||||
OCTET_LENGTH() Synonym for LENGTH()
|
||||
OR, || Logical OR
|
||||
ORD() Return character code for leftmost character of the argument
|
||||
PERCENT_RANK() Percentage rank value
|
||||
PERIOD_ADD() Add a period to a year-month
|
||||
PERIOD_DIFF() Return the number of months between periods
|
||||
PI() Return the value of pi
|
||||
Point() Construct Point from coordinates
|
||||
Polygon() Construct Polygon from LineString arguments
|
||||
POSITION() Synonym for LOCATE()
|
||||
POW() Return the argument raised to the specified power
|
||||
POWER() Return the argument raised to the specified power
|
||||
PS_CURRENT_THREAD_ID() Performance Schema thread ID for current thread 8.0.16
|
||||
PS_THREAD_ID() Performance Schema thread ID for given thread 8.0.16
|
||||
QUARTER() Return the quarter from a date argument
|
||||
QUOTE() Escape the argument for use in an SQL statement
|
||||
RADIANS() Return argument converted to radians
|
||||
RAND() Return a random floating-point value
|
||||
RANDOM_BYTES() Return a random byte vector
|
||||
RANK() Rank of current row within its partition, with gaps
|
||||
REGEXP Whether string matches regular expression
|
||||
REGEXP_INSTR() Starting index of substring matching regular expression
|
||||
REGEXP_LIKE() Whether string matches regular expression
|
||||
REGEXP_REPLACE() Replace substrings matching regular expression
|
||||
REGEXP_SUBSTR() Return substring matching regular expression
|
||||
RELEASE_ALL_LOCKS() Release all current named locks
|
||||
RELEASE_LOCK() Release the named lock
|
||||
REPEAT() Repeat a string the specified number of times
|
||||
REPLACE() Replace occurrences of a specified string
|
||||
REVERSE() Reverse the characters in a string
|
||||
RIGHT() Return the specified rightmost number of characters
|
||||
RLIKE Whether string matches regular expression
|
||||
ROLES_GRAPHML() Return a GraphML document representing memory role subgraphs
|
||||
ROUND() Round the argument
|
||||
ROW_COUNT() The number of rows updated
|
||||
ROW_NUMBER() Number of current row within its partition
|
||||
RPAD() Append string the specified number of times
|
||||
RTRIM() Remove trailing spaces
|
||||
SCHEMA() Synonym for DATABASE()
|
||||
SEC_TO_TIME() Converts seconds to 'hh:mm:ss' format
|
||||
SECOND() Return the second (0-59)
|
||||
SESSION_USER() Synonym for USER()
|
||||
SHA1(), SHA() Calculate an SHA-1 160-bit checksum
|
||||
SHA2() Calculate an SHA-2 checksum
|
||||
SIGN() Return the sign of the argument
|
||||
SIN() Return the sine of the argument
|
||||
SLEEP() Sleep for a number of seconds
|
||||
SOUNDEX() Return a soundex string
|
||||
SOUNDS LIKE Compare sounds
|
||||
SOURCE_POS_WAIT() Block until the replica has read and applied all updates up to the specified position 8.0.26
|
||||
SPACE() Return a string of the specified number of spaces
|
||||
SQRT() Return the square root of the argument
|
||||
ST_Area() Return Polygon or MultiPolygon area
|
||||
ST_AsBinary(), ST_AsWKB() Convert from internal geometry format to WKB
|
||||
ST_AsGeoJSON() Generate GeoJSON object from geometry
|
||||
ST_AsText(), ST_AsWKT() Convert from internal geometry format to WKT
|
||||
ST_Buffer() Return geometry of points within given distance from geometry
|
||||
ST_Buffer_Strategy() Produce strategy option for ST_Buffer()
|
||||
ST_Centroid() Return centroid as a point
|
||||
ST_Collect() Aggregate spatial values into collection 8.0.24
|
||||
ST_Contains() Whether one geometry contains another
|
||||
ST_ConvexHull() Return convex hull of geometry
|
||||
ST_Crosses() Whether one geometry crosses another
|
||||
ST_Difference() Return point set difference of two geometries
|
||||
ST_Dimension() Dimension of geometry
|
||||
ST_Disjoint() Whether one geometry is disjoint from another
|
||||
ST_Distance() The distance of one geometry from another
|
||||
ST_Distance_Sphere() Minimum distance on earth between two geometries
|
||||
ST_EndPoint() End Point of LineString
|
||||
ST_Envelope() Return MBR of geometry
|
||||
ST_Equals() Whether one geometry is equal to another
|
||||
ST_ExteriorRing() Return exterior ring of Polygon
|
||||
ST_FrechetDistance() The discrete Fréchet distance of one geometry from another 8.0.23
|
||||
ST_GeoHash() Produce a geohash value
|
||||
ST_GeomCollFromText(), ST_GeometryCollectionFromText(), ST_GeomCollFromTxt() Return geometry collection from WKT
|
||||
ST_GeomCollFromWKB(), ST_GeometryCollectionFromWKB() Return geometry collection from WKB
|
||||
ST_GeometryN() Return N-th geometry from geometry collection
|
||||
ST_GeometryType() Return name of geometry type
|
||||
ST_GeomFromGeoJSON() Generate geometry from GeoJSON object
|
||||
ST_GeomFromText(), ST_GeometryFromText() Return geometry from WKT
|
||||
ST_GeomFromWKB(), ST_GeometryFromWKB() Return geometry from WKB
|
||||
ST_HausdorffDistance() The discrete Hausdorff distance of one geometry from another 8.0.23
|
||||
ST_InteriorRingN() Return N-th interior ring of Polygon
|
||||
ST_Intersection() Return point set intersection of two geometries
|
||||
ST_Intersects() Whether one geometry intersects another
|
||||
ST_IsClosed() Whether a geometry is closed and simple
|
||||
ST_IsEmpty() Whether a geometry is empty
|
||||
ST_IsSimple() Whether a geometry is simple
|
||||
ST_IsValid() Whether a geometry is valid
|
||||
ST_LatFromGeoHash() Return latitude from geohash value
|
||||
ST_Latitude() Return latitude of Point 8.0.12
|
||||
ST_Length() Return length of LineString
|
||||
ST_LineFromText(), ST_LineStringFromText() Construct LineString from WKT
|
||||
ST_LineFromWKB(), ST_LineStringFromWKB() Construct LineString from WKB
|
||||
ST_LineInterpolatePoint() The point a given percentage along a LineString 8.0.24
|
||||
ST_LineInterpolatePoints() The points a given percentage along a LineString 8.0.24
|
||||
ST_LongFromGeoHash() Return longitude from geohash value
|
||||
ST_Longitude() Return longitude of Point 8.0.12
|
||||
ST_MakeEnvelope() Rectangle around two points
|
||||
ST_MLineFromText(), ST_MultiLineStringFromText() Construct MultiLineString from WKT
|
||||
ST_MLineFromWKB(), ST_MultiLineStringFromWKB() Construct MultiLineString from WKB
|
||||
ST_MPointFromText(), ST_MultiPointFromText() Construct MultiPoint from WKT
|
||||
ST_MPointFromWKB(), ST_MultiPointFromWKB() Construct MultiPoint from WKB
|
||||
ST_MPolyFromText(), ST_MultiPolygonFromText() Construct MultiPolygon from WKT
|
||||
ST_MPolyFromWKB(), ST_MultiPolygonFromWKB() Construct MultiPolygon from WKB
|
||||
ST_NumGeometries() Return number of geometries in geometry collection
|
||||
ST_NumInteriorRing(), ST_NumInteriorRings() Return number of interior rings in Polygon
|
||||
ST_NumPoints() Return number of points in LineString
|
||||
ST_Overlaps() Whether one geometry overlaps another
|
||||
ST_PointAtDistance() The point a given distance along a LineString 8.0.24
|
||||
ST_PointFromGeoHash() Convert geohash value to POINT value
|
||||
ST_PointFromText() Construct Point from WKT
|
||||
ST_PointFromWKB() Construct Point from WKB
|
||||
ST_PointN() Return N-th point from LineString
|
||||
ST_PolyFromText(), ST_PolygonFromText() Construct Polygon from WKT
|
||||
ST_PolyFromWKB(), ST_PolygonFromWKB() Construct Polygon from WKB
|
||||
ST_Simplify() Return simplified geometry
|
||||
ST_SRID() Return spatial reference system ID for geometry
|
||||
ST_StartPoint() Start Point of LineString
|
||||
ST_SwapXY() Return argument with X/Y coordinates swapped
|
||||
ST_SymDifference() Return point set symmetric difference of two geometries
|
||||
ST_Touches() Whether one geometry touches another
|
||||
ST_Transform() Transform coordinates of geometry 8.0.13
|
||||
ST_Union() Return point set union of two geometries
|
||||
ST_Validate() Return validated geometry
|
||||
ST_Within() Whether one geometry is within another
|
||||
ST_X() Return X coordinate of Point
|
||||
ST_Y() Return Y coordinate of Point
|
||||
STATEMENT_DIGEST() Compute statement digest hash value
|
||||
STATEMENT_DIGEST_TEXT() Compute normalized statement digest
|
||||
STD() Return the population standard deviation
|
||||
STDDEV() Return the population standard deviation
|
||||
STDDEV_POP() Return the population standard deviation
|
||||
STDDEV_SAMP() Return the sample standard deviation
|
||||
STR_TO_DATE() Convert a string to a date
|
||||
STRCMP() Compare two strings
|
||||
SUBDATE() Synonym for DATE_SUB() when invoked with three arguments
|
||||
SUBSTR() Return the substring as specified
|
||||
SUBSTRING() Return the substring as specified
|
||||
SUBSTRING_INDEX() Return a substring from a string before the specified number of occurrences of the delimiter
|
||||
SUBTIME() Subtract times
|
||||
SUM() Return the sum
|
||||
SYSDATE() Return the time at which the function executes
|
||||
SYSTEM_USER() Synonym for USER()
|
||||
TAN() Return the tangent of the argument
|
||||
TIME() Extract the time portion of the expression passed
|
||||
TIME_FORMAT() Format as time
|
||||
TIME_TO_SEC() Return the argument converted to seconds
|
||||
TIMEDIFF() Subtract time
|
||||
TIMESTAMP() With a single argument, this function returns the date or datetime expression; with two arguments, the sum of the arguments
|
||||
TIMESTAMPADD() Add an interval to a datetime expression
|
||||
TIMESTAMPDIFF() Return the difference of two datetime expressions, using the units specified
|
||||
TO_BASE64() Return the argument converted to a base-64 string
|
||||
TO_DAYS() Return the date argument converted to days
|
||||
TO_SECONDS() Return the date or datetime argument converted to seconds since Year 0
|
||||
TRIM() Remove leading and trailing spaces
|
||||
TRUNCATE() Truncate to specified number of decimal places
|
||||
UCASE() Synonym for UPPER()
|
||||
UNCOMPRESS() Uncompress a string compressed
|
||||
UNCOMPRESSED_LENGTH() Return the length of a string before compression
|
||||
UNHEX() Return a string containing hex representation of a number
|
||||
UNIX_TIMESTAMP() Return a Unix timestamp
|
||||
UpdateXML() Return replaced XML fragment
|
||||
UPPER() Convert to uppercase
|
||||
USER() The user name and host name provided by the client
|
||||
UTC_DATE() Return the current UTC date
|
||||
UTC_TIME() Return the current UTC time
|
||||
UTC_TIMESTAMP() Return the current UTC date and time
|
||||
UUID() Return a Universal Unique Identifier (UUID)
|
||||
UUID_SHORT() Return an integer-valued universal identifier
|
||||
UUID_TO_BIN() Convert string UUID to binary
|
||||
VALIDATE_PASSWORD_STRENGTH() Determine strength of password
|
||||
VALUES() Define the values to be used during an INSERT
|
||||
VAR_POP() Return the population standard variance
|
||||
VAR_SAMP() Return the sample variance
|
||||
VARIANCE() Return the population standard variance
|
||||
VERSION() Return a string that indicates the MySQL server version
|
||||
WAIT_FOR_EXECUTED_GTID_SET() Wait until the given GTIDs have executed on the replica.
|
||||
WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS() Use WAIT_FOR_EXECUTED_GTID_SET(). 8.0.18
|
||||
WEEK() Return the week number
|
||||
WEEKDAY() Return the weekday index
|
||||
WEEKOFYEAR() Return the calendar week of the date (1-53)
|
||||
WEIGHT_STRING() Return the weight string for a string
|
||||
XOR Logical XOR
|
||||
YEAR() Return the year
|
||||
YEARWEEK() Return the year and week
|
|
113
src/harlequin_mysql/interactions.py
Normal file
113
src/harlequin_mysql/interactions.py
Normal file
|
@ -0,0 +1,113 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from textwrap import dedent
|
||||
from typing import TYPE_CHECKING, Literal, Sequence
|
||||
|
||||
from harlequin.catalog import CatalogItem
|
||||
from harlequin.exception import HarlequinQueryError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from harlequin.driver import HarlequinDriver
|
||||
|
||||
from harlequin_mysql.catalog import (
|
||||
ColumnCatalogItem,
|
||||
DatabaseCatalogItem,
|
||||
RelationCatalogItem,
|
||||
)
|
||||
|
||||
|
||||
def execute_use_statement(
|
||||
item: "DatabaseCatalogItem",
|
||||
driver: "HarlequinDriver",
|
||||
) -> None:
|
||||
if item.connection is None:
|
||||
return
|
||||
try:
|
||||
item.connection.execute(f"use {item.label}")
|
||||
except HarlequinQueryError:
|
||||
driver.notify("Could not switch context", severity="error")
|
||||
raise
|
||||
else:
|
||||
driver.notify(f"Editor context switched to {item.label}")
|
||||
|
||||
|
||||
def execute_drop_database_statement(
|
||||
item: "DatabaseCatalogItem",
|
||||
driver: "HarlequinDriver",
|
||||
) -> None:
|
||||
def _drop_database() -> None:
|
||||
if item.connection is None:
|
||||
return
|
||||
try:
|
||||
item.connection.execute(f"drop database {item.qualified_identifier}")
|
||||
except HarlequinQueryError:
|
||||
driver.notify(f"Could not drop database {item.label}", severity="error")
|
||||
raise
|
||||
else:
|
||||
driver.notify(f"Dropped database {item.label}")
|
||||
driver.refresh_catalog()
|
||||
|
||||
if item.children or item.fetch_children():
|
||||
driver.confirm_and_execute(callback=_drop_database)
|
||||
else:
|
||||
_drop_database()
|
||||
|
||||
|
||||
def execute_drop_relation_statement(
|
||||
item: "RelationCatalogItem",
|
||||
driver: "HarlequinDriver",
|
||||
relation_type: Literal["view", "table", "foreign table"],
|
||||
) -> None:
|
||||
def _drop_relation() -> None:
|
||||
if item.connection is None:
|
||||
return
|
||||
try:
|
||||
item.connection.execute(f"drop {relation_type} {item.qualified_identifier}")
|
||||
except HarlequinQueryError:
|
||||
driver.notify(
|
||||
f"Could not drop {relation_type} {item.label}", severity="error"
|
||||
)
|
||||
raise
|
||||
else:
|
||||
driver.notify(f"Dropped {relation_type} {item.label}")
|
||||
driver.refresh_catalog()
|
||||
|
||||
driver.confirm_and_execute(callback=_drop_relation)
|
||||
|
||||
|
||||
def execute_drop_table_statement(
|
||||
item: "RelationCatalogItem", driver: "HarlequinDriver"
|
||||
) -> None:
|
||||
execute_drop_relation_statement(item=item, driver=driver, relation_type="table")
|
||||
|
||||
|
||||
def execute_drop_view_statement(
|
||||
item: "RelationCatalogItem", driver: "HarlequinDriver"
|
||||
) -> None:
|
||||
execute_drop_relation_statement(item=item, driver=driver, relation_type="view")
|
||||
|
||||
|
||||
def show_select_star(
|
||||
item: "RelationCatalogItem",
|
||||
driver: "HarlequinDriver",
|
||||
) -> None:
|
||||
driver.insert_text_in_new_buffer(
|
||||
dedent(
|
||||
f"""
|
||||
select *
|
||||
from {item.qualified_identifier}
|
||||
limit 100
|
||||
""".strip("\n")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def insert_columns_at_cursor(
|
||||
item: "RelationCatalogItem",
|
||||
driver: "HarlequinDriver",
|
||||
) -> None:
|
||||
if item.loaded:
|
||||
cols: Sequence["CatalogItem" | "ColumnCatalogItem"] = item.children
|
||||
else:
|
||||
cols = item.fetch_children()
|
||||
driver.insert_text_at_selection(text=",\n".join(c.query_name for c in cols))
|
892
src/harlequin_mysql/keywords.csv
Normal file
892
src/harlequin_mysql/keywords.csv
Normal file
|
@ -0,0 +1,892 @@
|
|||
ACCESSIBLE,False,False
|
||||
ACCOUNT,False,False
|
||||
ACTION,False,False
|
||||
ACTIVE,False,False
|
||||
ADD,False,False
|
||||
ADMIN,False,False
|
||||
AFTER,False,False
|
||||
AGAINST,False,False
|
||||
AGGREGATE,False,False
|
||||
ALGORITHM,False,False
|
||||
ALL,False,False
|
||||
ALTER,False,False
|
||||
ALWAYS,False,False
|
||||
ANALYSE,False,True
|
||||
ANALYZE,False,False
|
||||
AND,False,False
|
||||
ANY,False,False
|
||||
ARRAY,False,False
|
||||
AS,False,False
|
||||
ASC,False,False
|
||||
ASCII,False,False
|
||||
ASENSITIVE,False,False
|
||||
AT,False,False
|
||||
ATTRIBUTE,False,False
|
||||
AUTHENTICATION,False,False
|
||||
AUTOEXTEND_SIZE,False,False
|
||||
AUTO_INCREMENT,False,False
|
||||
AVG,False,False
|
||||
AVG_ROW_LENGTH,False,False
|
||||
BACKUP,False,False
|
||||
BEFORE,False,False
|
||||
BEGIN,False,False
|
||||
BETWEEN,False,False
|
||||
BIGINT,False,False
|
||||
BINARY,False,False
|
||||
BINLOG,False,False
|
||||
BIT,False,False
|
||||
BLOB,False,False
|
||||
BLOCK,False,False
|
||||
BOOL,False,False
|
||||
BOOLEAN,False,False
|
||||
BOTH,False,False
|
||||
BTREE,False,False
|
||||
BUCKETS,False,False
|
||||
BULK,False,False
|
||||
BY,False,False
|
||||
BYTE,False,False
|
||||
CACHE,False,False
|
||||
CALL,False,False
|
||||
CASCADE,False,False
|
||||
CASCADED,False,False
|
||||
CASE,False,False
|
||||
CATALOG_NAME,False,False
|
||||
CHAIN,False,False
|
||||
CHALLENGE_RESPONSE,False,False
|
||||
CHANGE,False,False
|
||||
CHANGED,False,False
|
||||
CHANNEL,False,False
|
||||
CHAR,False,False
|
||||
CHARACTER,False,False
|
||||
CHARSET,False,False
|
||||
CHECK,False,False
|
||||
CHECKSUM,False,False
|
||||
CIPHER,False,False
|
||||
CLASS_ORIGIN,False,False
|
||||
CLIENT,False,False
|
||||
CLONE,False,False
|
||||
CLOSE,False,False
|
||||
COALESCE,False,False
|
||||
CODE,False,False
|
||||
COLLATE,False,False
|
||||
COLLATION,False,False
|
||||
COLUMN,False,False
|
||||
COLUMNS,False,False
|
||||
COLUMN_FORMAT,False,False
|
||||
COLUMN_NAME,False,False
|
||||
COMMENT,False,False
|
||||
COMMIT,False,False
|
||||
COMMITTED,False,False
|
||||
COMPACT,False,False
|
||||
COMPLETION,False,False
|
||||
COMPONENT,False,False
|
||||
COMPRESSED,False,False
|
||||
COMPRESSION,False,False
|
||||
CONCURRENT,False,False
|
||||
CONDITION,False,False
|
||||
CONNECTION,False,False
|
||||
CONSISTENT,False,False
|
||||
CONSTRAINT,False,False
|
||||
CONSTRAINT_CATALOG,False,False
|
||||
CONSTRAINT_NAME,False,False
|
||||
CONSTRAINT_SCHEMA,False,False
|
||||
CONTAINS,False,False
|
||||
CONTEXT,False,False
|
||||
CONTINUE,False,False
|
||||
CONVERT,False,False
|
||||
CPU,False,False
|
||||
CREATE,False,False
|
||||
CROSS,False,False
|
||||
CUBE,False,False
|
||||
CUME_DIST,False,False
|
||||
CURRENT,False,False
|
||||
CURRENT_DATE,False,False
|
||||
CURRENT_TIME,False,False
|
||||
CURRENT_TIMESTAMP,False,False
|
||||
CURRENT_USER,False,False
|
||||
CURSOR,False,False
|
||||
CURSOR_NAME,False,False
|
||||
DATA,False,False
|
||||
DATABASE,False,False
|
||||
DATABASES,False,False
|
||||
DATAFILE,False,False
|
||||
DATE,False,False
|
||||
DATETIME,False,False
|
||||
DAY,False,False
|
||||
DAY_HOUR,False,False
|
||||
DAY_MICROSECOND,False,False
|
||||
DAY_MINUTE,False,False
|
||||
DAY_SECOND,False,False
|
||||
DEALLOCATE,False,False
|
||||
DEC,False,False
|
||||
DECIMAL,False,False
|
||||
DECLARE,False,False
|
||||
DEFAULT,False,False
|
||||
DEFAULT_AUTH,False,False
|
||||
DEFINER,False,False
|
||||
DEFINITION,False,False
|
||||
DELAYED,False,False
|
||||
DELAY_KEY_WRITE,False,False
|
||||
DELETE,False,False
|
||||
DENSE_RANK,False,False
|
||||
DESC,False,False
|
||||
DESCRIBE,False,False
|
||||
DESCRIPTION,False,False
|
||||
DES_KEY_FILE,False,True
|
||||
DETERMINISTIC,False,False
|
||||
DIAGNOSTICS,False,False
|
||||
DIRECTORY,False,False
|
||||
DISABLE,False,False
|
||||
DISCARD,False,False
|
||||
DISK,False,False
|
||||
DISTINCT,False,False
|
||||
DISTINCTROW,False,False
|
||||
DIV,False,False
|
||||
DO,False,False
|
||||
DOUBLE,False,False
|
||||
DROP,False,False
|
||||
DUAL,False,False
|
||||
DUMPFILE,False,False
|
||||
DUPLICATE,False,False
|
||||
DYNAMIC,False,False
|
||||
EACH,False,False
|
||||
ELSE,False,False
|
||||
ELSEIF,False,False
|
||||
EMPTY,False,False
|
||||
ENABLE,False,False
|
||||
ENCLOSED,False,False
|
||||
ENCRYPTION,False,False
|
||||
END,False,False
|
||||
ENDS,False,False
|
||||
ENFORCED,False,False
|
||||
ENGINE,False,False
|
||||
ENGINES,False,False
|
||||
ENGINE_ATTRIBUTE,False,False
|
||||
ENUM,False,False
|
||||
ERROR,False,False
|
||||
ERRORS,False,False
|
||||
ESCAPE,False,False
|
||||
ESCAPED,False,False
|
||||
EVENT,False,False
|
||||
EVENTS,False,False
|
||||
EVERY,False,False
|
||||
EXCEPT,False,False
|
||||
EXCHANGE,False,False
|
||||
EXCLUDE,False,False
|
||||
EXECUTE,False,False
|
||||
EXISTS,False,False
|
||||
EXIT,False,False
|
||||
EXPANSION,False,False
|
||||
EXPIRE,False,False
|
||||
EXPLAIN,False,False
|
||||
EXPORT,False,False
|
||||
EXTENDED,False,False
|
||||
EXTENT_SIZE,False,False
|
||||
FACTOR,False,False
|
||||
FAILED_LOGIN_ATTEMPTS,False,False
|
||||
FALSE,False,False
|
||||
FAST,False,False
|
||||
FAULTS,False,False
|
||||
FETCH,False,False
|
||||
FIELDS,False,False
|
||||
FILE,False,False
|
||||
FILE_BLOCK_SIZE,False,False
|
||||
FILTER,False,False
|
||||
FINISH,False,False
|
||||
FIRST,False,False
|
||||
FIRST_VALUE,False,False
|
||||
FIXED,False,False
|
||||
FLOAT,False,False
|
||||
FLOAT4,False,False
|
||||
FLOAT8,False,False
|
||||
FLUSH,False,False
|
||||
FOLLOWING,False,False
|
||||
FOLLOWS,False,False
|
||||
FOR,False,False
|
||||
FORCE,False,False
|
||||
FOREIGN,False,False
|
||||
FORMAT,False,False
|
||||
FOUND,False,False
|
||||
FROM,False,False
|
||||
FULL,False,False
|
||||
FULLTEXT,False,False
|
||||
FUNCTION,False,False
|
||||
GENERAL,False,False
|
||||
GENERATE,False,False
|
||||
GENERATED,False,False
|
||||
GEOMCOLLECTION,False,False
|
||||
GEOMETRY,False,False
|
||||
GEOMETRYCOLLECTION,False,False
|
||||
GET,False,False
|
||||
GET_FORMAT,False,False
|
||||
GET_MASTER_PUBLIC_KEY,False,False
|
||||
GET_SOURCE_PUBLIC_KEY,False,False
|
||||
GLOBAL,False,False
|
||||
GRANT,False,False
|
||||
GRANTS,False,False
|
||||
GROUP,False,False
|
||||
GROUPING,False,False
|
||||
GROUPS,False,False
|
||||
GROUP_REPLICATION,False,False
|
||||
GTID_ONLY,False,False
|
||||
HANDLER,False,False
|
||||
HASH,False,False
|
||||
HAVING,False,False
|
||||
HELP,False,False
|
||||
HIGH_PRIORITY,False,False
|
||||
HISTOGRAM,False,False
|
||||
HISTORY,False,False
|
||||
HOST,False,False
|
||||
HOSTS,False,False
|
||||
HOUR,False,False
|
||||
HOUR_MICROSECOND,False,False
|
||||
HOUR_MINUTE,False,False
|
||||
HOUR_SECOND,False,False
|
||||
IDENTIFIED,False,False
|
||||
IF,False,False
|
||||
IGNORE,False,False
|
||||
IGNORE_SERVER_IDS,False,False
|
||||
IMPORT,False,False
|
||||
IN,False,False
|
||||
INACTIVE,False,False
|
||||
INDEX,False,False
|
||||
INDEXES,False,False
|
||||
INFILE,False,False
|
||||
INITIAL,False,False
|
||||
INITIAL_SIZE,False,False
|
||||
INITIATE,False,False
|
||||
INNER,False,False
|
||||
INOUT,False,False
|
||||
INSENSITIVE,False,False
|
||||
INSERT,False,False
|
||||
INSERT_METHOD,False,False
|
||||
INSTALL,False,False
|
||||
INSTANCE,False,False
|
||||
INT,False,False
|
||||
INT1,False,False
|
||||
INT2,False,False
|
||||
INT3,False,False
|
||||
INT4,False,False
|
||||
INT8,False,False
|
||||
INTEGER,False,False
|
||||
INTERSECT,False,False
|
||||
INTERVAL,False,False
|
||||
INTO,False,False
|
||||
INVISIBLE,False,False
|
||||
INVOKER,False,False
|
||||
IO,False,False
|
||||
IO_AFTER_GTIDS,False,False
|
||||
IO_BEFORE_GTIDS,False,False
|
||||
IO_THREAD,False,False
|
||||
IPC,False,False
|
||||
IS,False,False
|
||||
ISOLATION,False,False
|
||||
ISSUER,False,False
|
||||
ITERATE,False,False
|
||||
JOIN,False,False
|
||||
JSON,False,False
|
||||
JSON_TABLE,False,False
|
||||
JSON_VALUE,False,False
|
||||
KEY,False,False
|
||||
KEYRING,False,False
|
||||
KEYS,False,False
|
||||
KEY_BLOCK_SIZE,False,False
|
||||
KILL,False,False
|
||||
LAG,False,False
|
||||
LANGUAGE,False,False
|
||||
LAST,False,False
|
||||
LAST_VALUE,False,False
|
||||
LATERAL,False,False
|
||||
LEAD,False,False
|
||||
LEADING,False,False
|
||||
LEAVE,False,False
|
||||
LEAVES,False,False
|
||||
LEFT,False,False
|
||||
LESS,False,False
|
||||
LEVEL,False,False
|
||||
LIKE,False,False
|
||||
LIMIT,False,False
|
||||
LINEAR,False,False
|
||||
LINES,False,False
|
||||
LINESTRING,False,False
|
||||
LIST,False,False
|
||||
LOAD,False,False
|
||||
LOCAL,False,False
|
||||
LOCALTIME,False,False
|
||||
LOCALTIMESTAMP,False,False
|
||||
LOCK,False,False
|
||||
LOCKED,False,False
|
||||
LOCKS,False,False
|
||||
LOGFILE,False,False
|
||||
LOGS,False,False
|
||||
LONG,False,False
|
||||
LONGBLOB,False,False
|
||||
LONGTEXT,False,False
|
||||
LOOP,False,False
|
||||
LOW_PRIORITY,False,False
|
||||
MASTER,False,False
|
||||
MASTER_AUTO_POSITION,False,False
|
||||
MASTER_BIND,False,False
|
||||
MASTER_COMPRESSION_ALGORITHMS,False,False
|
||||
MASTER_CONNECT_RETRY,False,False
|
||||
MASTER_DELAY,False,False
|
||||
MASTER_HEARTBEAT_PERIOD,False,False
|
||||
MASTER_HOST,False,False
|
||||
MASTER_LOG_FILE,False,False
|
||||
MASTER_LOG_POS,False,False
|
||||
MASTER_PASSWORD,False,False
|
||||
MASTER_PORT,False,False
|
||||
MASTER_PUBLIC_KEY_PATH,False,False
|
||||
MASTER_RETRY_COUNT,False,False
|
||||
MASTER_SERVER_ID,False,True
|
||||
MASTER_SSL,False,False
|
||||
MASTER_SSL_CA,False,False
|
||||
MASTER_SSL_CAPATH,False,False
|
||||
MASTER_SSL_CERT,False,False
|
||||
MASTER_SSL_CIPHER,False,False
|
||||
MASTER_SSL_CRL,False,False
|
||||
MASTER_SSL_CRLPATH,False,False
|
||||
MASTER_SSL_KEY,False,False
|
||||
MASTER_SSL_VERIFY_SERVER_CERT,False,False
|
||||
MASTER_TLS_CIPHERSUITES,False,False
|
||||
MASTER_TLS_VERSION,False,False
|
||||
MASTER_USER,False,False
|
||||
MASTER_ZSTD_COMPRESSION_LEVEL,False,False
|
||||
MATCH,False,False
|
||||
MAXVALUE,False,False
|
||||
MAX_CONNECTIONS_PER_HOUR,False,False
|
||||
MAX_QUERIES_PER_HOUR,False,False
|
||||
MAX_ROWS,False,False
|
||||
MAX_SIZE,False,False
|
||||
MAX_UPDATES_PER_HOUR,False,False
|
||||
MAX_USER_CONNECTIONS,False,False
|
||||
MEDIUM,False,False
|
||||
MEDIUMBLOB,False,False
|
||||
MEDIUMINT,False,False
|
||||
MEDIUMTEXT,False,False
|
||||
MEMBER,False,False
|
||||
MEMORY,False,False
|
||||
MERGE,False,False
|
||||
MESSAGE_TEXT,False,False
|
||||
MICROSECOND,False,False
|
||||
MIDDLEINT,False,False
|
||||
MIGRATE,False,False
|
||||
MINUTE,False,False
|
||||
MINUTE_MICROSECOND,False,False
|
||||
MINUTE_SECOND,False,False
|
||||
MIN_ROWS,False,False
|
||||
MOD,False,False
|
||||
MODE,False,False
|
||||
MODIFIES,False,False
|
||||
MODIFY,False,False
|
||||
MONTH,False,False
|
||||
MULTILINESTRING,False,False
|
||||
MULTIPOINT,False,False
|
||||
MULTIPOLYGON,False,False
|
||||
MUTEX,False,False
|
||||
MYSQL_ERRNO,False,False
|
||||
NAME,False,False
|
||||
NAMES,False,False
|
||||
NATIONAL,False,False
|
||||
NATURAL,False,False
|
||||
NCHAR,False,False
|
||||
NDB,False,False
|
||||
NDBCLUSTER,False,False
|
||||
NESTED,False,False
|
||||
NETWORK_NAMESPACE,False,False
|
||||
NEVER,False,False
|
||||
NEW,False,False
|
||||
NEXT,False,False
|
||||
NO,False,False
|
||||
NODEGROUP,False,False
|
||||
NONE,False,False
|
||||
NOT,False,False
|
||||
NOWAIT,False,False
|
||||
NO_WAIT,False,False
|
||||
NO_WRITE_TO_BINLOG,False,False
|
||||
NTH_VALUE,False,False
|
||||
NTILE,False,False
|
||||
NULL,False,False
|
||||
NULLS,False,False
|
||||
NUMBER,False,False
|
||||
NUMERIC,False,False
|
||||
NVARCHAR,False,False
|
||||
OF,False,False
|
||||
OFF,False,False
|
||||
OFFSET,False,False
|
||||
OJ,False,False
|
||||
OLD,False,False
|
||||
ON,False,False
|
||||
ONE,False,False
|
||||
ONLY,False,False
|
||||
OPEN,False,False
|
||||
OPTIMIZE,False,False
|
||||
OPTIMIZER_COSTS,False,False
|
||||
OPTION,False,False
|
||||
OPTIONAL,False,False
|
||||
OPTIONALLY,False,False
|
||||
OPTIONS,False,False
|
||||
OR,False,False
|
||||
ORDER,False,False
|
||||
ORDINALITY,False,False
|
||||
ORGANIZATION,False,False
|
||||
OTHERS,False,False
|
||||
OUT,False,False
|
||||
OUTER,False,False
|
||||
OUTFILE,False,False
|
||||
OVER,False,False
|
||||
OWNER,False,False
|
||||
PACK_KEYS,False,False
|
||||
PAGE,False,False
|
||||
PARSER,False,False
|
||||
PARTIAL,False,False
|
||||
PARTITION,False,False
|
||||
PARTITIONING,False,False
|
||||
PARTITIONS,False,False
|
||||
PASSWORD,False,False
|
||||
PASSWORD_LOCK_TIME,False,False
|
||||
PATH,False,False
|
||||
PERCENT_RANK,False,False
|
||||
PERSIST,False,False
|
||||
PERSIST_ONLY,False,False
|
||||
PHASE,False,False
|
||||
PLUGIN,False,False
|
||||
PLUGINS,False,False
|
||||
PLUGIN_DIR,False,False
|
||||
POINT,False,False
|
||||
POLYGON,False,False
|
||||
PORT,False,False
|
||||
PRECEDES,False,False
|
||||
PRECEDING,False,False
|
||||
PRECISION,False,False
|
||||
PREPARE,False,False
|
||||
PRESERVE,False,False
|
||||
PREV,False,False
|
||||
PRIMARY,False,False
|
||||
PRIVILEGES,False,False
|
||||
PRIVILEGE_CHECKS_USER,False,False
|
||||
PROCEDURE,False,False
|
||||
PROCESS,False,False
|
||||
PROCESSLIST,False,False
|
||||
PROFILE,False,False
|
||||
PROFILES,False,False
|
||||
PROXY,False,False
|
||||
PURGE,False,False
|
||||
QUARTER,False,False
|
||||
QUERY,False,False
|
||||
QUICK,False,False
|
||||
RANDOM,False,False
|
||||
RANGE,False,False
|
||||
RANK,False,False
|
||||
READ,False,False
|
||||
READS,False,False
|
||||
READ_ONLY,False,False
|
||||
READ_WRITE,False,False
|
||||
REAL,False,False
|
||||
REBUILD,False,False
|
||||
RECOVER,False,False
|
||||
RECURSIVE,False,False
|
||||
REDOFILE,False,True
|
||||
REDO_BUFFER_SIZE,False,False
|
||||
REDUNDANT,False,False
|
||||
REFERENCE,False,False
|
||||
REFERENCES,False,False
|
||||
REGEXP,False,False
|
||||
REGISTRATION,False,False
|
||||
RELAY,False,False
|
||||
RELAYLOG,False,False
|
||||
RELAY_LOG_FILE,False,False
|
||||
RELAY_LOG_POS,False,False
|
||||
RELAY_THREAD,False,False
|
||||
RELEASE,False,False
|
||||
RELOAD,False,False
|
||||
REMOTE,False,True
|
||||
REMOVE,False,False
|
||||
RENAME,False,False
|
||||
REORGANIZE,False,False
|
||||
REPAIR,False,False
|
||||
REPEAT,False,False
|
||||
REPEATABLE,False,False
|
||||
REPLACE,False,False
|
||||
REPLICA,False,False
|
||||
REPLICAS,False,False
|
||||
REPLICATE_DO_DB,False,False
|
||||
REPLICATE_DO_TABLE,False,False
|
||||
REPLICATE_IGNORE_DB,False,False
|
||||
REPLICATE_IGNORE_TABLE,False,False
|
||||
REPLICATE_REWRITE_DB,False,False
|
||||
REPLICATE_WILD_DO_TABLE,False,False
|
||||
REPLICATE_WILD_IGNORE_TABLE,False,False
|
||||
REPLICATION,False,False
|
||||
REQUIRE,False,False
|
||||
REQUIRE_ROW_FORMAT,False,False
|
||||
RESET,False,False
|
||||
RESIGNAL,False,False
|
||||
RESOURCE,False,False
|
||||
RESPECT,False,False
|
||||
RESTART,False,False
|
||||
RESTORE,False,False
|
||||
RESTRICT,False,False
|
||||
RESUME,False,False
|
||||
RETAIN,False,False
|
||||
RETURN,False,False
|
||||
RETURNED_SQLSTATE,False,False
|
||||
RETURNING,False,False
|
||||
RETURNS,False,False
|
||||
REUSE,False,False
|
||||
REVERSE,False,False
|
||||
REVOKE,False,False
|
||||
RIGHT,False,False
|
||||
RLIKE,False,False
|
||||
ROLE,False,False
|
||||
ROLLBACK,False,False
|
||||
ROLLUP,False,False
|
||||
ROTATE,False,False
|
||||
ROUTINE,False,False
|
||||
ROW,False,False
|
||||
ROWS,False,False
|
||||
ROW_COUNT,False,False
|
||||
ROW_FORMAT,False,False
|
||||
ROW_NUMBER,False,False
|
||||
RTREE,False,False
|
||||
SAVEPOINT,False,False
|
||||
SCHEDULE,False,False
|
||||
SCHEMA,False,False
|
||||
SCHEMAS,False,False
|
||||
SCHEMA_NAME,False,False
|
||||
SECOND,False,False
|
||||
SECONDARY,False,False
|
||||
SECONDARY_ENGINE,False,False
|
||||
SECONDARY_ENGINE_ATTRIBUTE,False,False
|
||||
SECONDARY_LOAD,False,False
|
||||
SECONDARY_UNLOAD,False,False
|
||||
SECOND_MICROSECOND,False,False
|
||||
SECURITY,False,False
|
||||
SELECT,False,False
|
||||
SENSITIVE,False,False
|
||||
SEPARATOR,False,False
|
||||
SERIAL,False,False
|
||||
SERIALIZABLE,False,False
|
||||
SERVER,False,False
|
||||
SESSION,False,False
|
||||
SET,False,False
|
||||
SHARE,False,False
|
||||
SHOW,False,False
|
||||
SHUTDOWN,False,False
|
||||
SIGNAL,False,False
|
||||
SIGNED,False,False
|
||||
SIMPLE,False,False
|
||||
SKIP,False,False
|
||||
SLAVE,False,False
|
||||
SLOW,False,False
|
||||
SMALLINT,False,False
|
||||
SNAPSHOT,False,False
|
||||
SOCKET,False,False
|
||||
SOME,False,False
|
||||
SONAME,False,False
|
||||
SOUNDS,False,False
|
||||
SOURCE,False,False
|
||||
SOURCE_AUTO_POSITION,False,False
|
||||
SOURCE_BIND,False,False
|
||||
SOURCE_COMPRESSION_ALGORITHMS,False,False
|
||||
SOURCE_CONNECT_RETRY,False,False
|
||||
SOURCE_DELAY,False,False
|
||||
SOURCE_HEARTBEAT_PERIOD,False,False
|
||||
SOURCE_HOST,False,False
|
||||
SOURCE_LOG_FILE,False,False
|
||||
SOURCE_LOG_POS,False,False
|
||||
SOURCE_PASSWORD,False,False
|
||||
SOURCE_PORT,False,False
|
||||
SOURCE_PUBLIC_KEY_PATH,False,False
|
||||
SOURCE_RETRY_COUNT,False,False
|
||||
SOURCE_SSL,False,False
|
||||
SOURCE_SSL_CA,False,False
|
||||
SOURCE_SSL_CAPATH,False,False
|
||||
SOURCE_SSL_CERT,False,False
|
||||
SOURCE_SSL_CIPHER,False,False
|
||||
SOURCE_SSL_CRL,False,False
|
||||
SOURCE_SSL_CRLPATH,False,False
|
||||
SOURCE_SSL_KEY,False,False
|
||||
SOURCE_SSL_VERIFY_SERVER_CERT,False,False
|
||||
SOURCE_TLS_CIPHERSUITES,False,False
|
||||
SOURCE_TLS_VERSION,False,False
|
||||
SOURCE_USER,False,False
|
||||
SOURCE_ZSTD_COMPRESSION_LEVEL,False,False
|
||||
SPATIAL,False,False
|
||||
SPECIFIC,False,False
|
||||
SQL,False,False
|
||||
SQLEXCEPTION,False,False
|
||||
SQLSTATE,False,False
|
||||
SQLWARNING,False,False
|
||||
SQL_AFTER_GTIDS,False,False
|
||||
SQL_AFTER_MTS_GAPS,False,False
|
||||
SQL_BEFORE_GTIDS,False,False
|
||||
SQL_BIG_RESULT,False,False
|
||||
SQL_BUFFER_RESULT,False,False
|
||||
SQL_CACHE,False,True
|
||||
SQL_CALC_FOUND_ROWS,False,False
|
||||
SQL_NO_CACHE,False,False
|
||||
SQL_SMALL_RESULT,False,False
|
||||
SQL_THREAD,False,False
|
||||
SQL_TSI_DAY,False,False
|
||||
SQL_TSI_HOUR,False,False
|
||||
SQL_TSI_MINUTE,False,False
|
||||
SQL_TSI_MONTH,False,False
|
||||
SQL_TSI_QUARTER,False,False
|
||||
SQL_TSI_SECOND,False,False
|
||||
SQL_TSI_WEEK,False,False
|
||||
SQL_TSI_YEAR,False,False
|
||||
SRID,False,False
|
||||
SSL,False,False
|
||||
STACKED,False,False
|
||||
START,False,False
|
||||
STARTING,False,False
|
||||
STARTS,False,False
|
||||
STATS_AUTO_RECALC,False,False
|
||||
STATS_PERSISTENT,False,False
|
||||
STATS_SAMPLE_PAGES,False,False
|
||||
STATUS,False,False
|
||||
STOP,False,False
|
||||
STORAGE,False,False
|
||||
STORED,False,False
|
||||
STRAIGHT_JOIN,False,False
|
||||
STREAM,False,False
|
||||
STRING,False,False
|
||||
SUBCLASS_ORIGIN,False,False
|
||||
SUBJECT,False,False
|
||||
SUBPARTITION,False,False
|
||||
SUBPARTITIONS,False,False
|
||||
SUPER,False,False
|
||||
SUSPEND,False,False
|
||||
SWAPS,False,False
|
||||
SWITCHES,False,False
|
||||
SYSTEM,False,False
|
||||
TABLE,False,False
|
||||
TABLES,False,False
|
||||
TABLESPACE,False,False
|
||||
TABLE_CHECKSUM,False,False
|
||||
TABLE_NAME,False,False
|
||||
TEMPORARY,False,False
|
||||
TEMPTABLE,False,False
|
||||
TERMINATED,False,False
|
||||
TEXT,False,False
|
||||
THAN,False,False
|
||||
THEN,False,False
|
||||
THREAD_PRIORITY,False,False
|
||||
TIES,False,False
|
||||
TIME,False,False
|
||||
TIMESTAMP,False,False
|
||||
TIMESTAMPADD,False,False
|
||||
TIMESTAMPDIFF,False,False
|
||||
TINYBLOB,False,False
|
||||
TINYINT,False,False
|
||||
TINYTEXT,False,False
|
||||
TLS,False,False
|
||||
TO,False,False
|
||||
TRAILING,False,False
|
||||
TRANSACTION,False,False
|
||||
TRIGGER,False,False
|
||||
TRIGGERS,False,False
|
||||
TRUE,False,False
|
||||
TRUNCATE,False,False
|
||||
TYPE,False,False
|
||||
TYPES,False,False
|
||||
UNBOUNDED,False,False
|
||||
UNCOMMITTED,False,False
|
||||
UNDEFINED,False,False
|
||||
UNDO,False,False
|
||||
UNDOFILE,False,False
|
||||
UNDO_BUFFER_SIZE,False,False
|
||||
UNICODE,False,False
|
||||
UNINSTALL,False,False
|
||||
UNION,False,False
|
||||
UNIQUE,False,False
|
||||
UNKNOWN,False,False
|
||||
UNLOCK,False,False
|
||||
UNREGISTER,False,False
|
||||
UNSIGNED,False,False
|
||||
UNTIL,False,False
|
||||
UPDATE,False,False
|
||||
UPGRADE,False,False
|
||||
URL,False,False
|
||||
USAGE,False,False
|
||||
USE,False,False
|
||||
USER,False,False
|
||||
USER_RESOURCES,False,False
|
||||
USE_FRM,False,False
|
||||
USING,False,False
|
||||
UTC_DATE,False,False
|
||||
UTC_TIME,False,False
|
||||
UTC_TIMESTAMP,False,False
|
||||
VALIDATION,False,False
|
||||
VALUE,False,False
|
||||
VALUES,False,False
|
||||
VARBINARY,False,False
|
||||
VARCHAR,False,False
|
||||
VARCHARACTER,False,False
|
||||
VARIABLES,False,False
|
||||
VARYING,False,False
|
||||
VCPU,False,False
|
||||
VIEW,False,False
|
||||
VIRTUAL,False,False
|
||||
VISIBLE,False,False
|
||||
WAIT,False,False
|
||||
WARNINGS,False,False
|
||||
WEEK,False,False
|
||||
WEIGHT_STRING,False,False
|
||||
WHEN,False,False
|
||||
WHERE,False,False
|
||||
WHILE,False,False
|
||||
WINDOW,False,False
|
||||
WITH,False,False
|
||||
WITHOUT,False,False
|
||||
WORK,False,False
|
||||
WRAPPER,False,False
|
||||
WRITE,False,False
|
||||
X509,False,False
|
||||
XA,False,False
|
||||
XID,False,False
|
||||
XML,False,False
|
||||
XOR,False,False
|
||||
YEAR,False,False
|
||||
YEAR_MONTH,False,False
|
||||
ZEROFILL,False,False
|
||||
ZONE,False,False
|
||||
MySQL,False,False
|
||||
The,False,False
|
||||
A,False,False
|
||||
ACTIVE,False,False
|
||||
ADMIN,False,False
|
||||
ARRAY,False,False
|
||||
ATTRIBUTE,False,False
|
||||
AUTHENTICATION,False,False
|
||||
BUCKETS,False,False
|
||||
BULK,False,False
|
||||
CHALLENGE_RESPONSE,False,False
|
||||
CLONE,False,False
|
||||
COMPONENT,False,False
|
||||
CUME_DIST,False,False
|
||||
DEFINITION,False,False
|
||||
DENSE_RANK,False,False
|
||||
DESCRIPTION,False,False
|
||||
EMPTY,False,False
|
||||
ENFORCED,False,False
|
||||
ENGINE_ATTRIBUTE,False,False
|
||||
EXCEPT,False,False
|
||||
EXCLUDE,False,False
|
||||
FACTOR,False,False
|
||||
FAILED_LOGIN_ATTEMPTS,False,False
|
||||
FINISH,False,False
|
||||
FIRST_VALUE,False,False
|
||||
FOLLOWING,False,False
|
||||
GENERATE,False,False
|
||||
GEOMCOLLECTION,False,False
|
||||
GET_MASTER_PUBLIC_KEY,False,False
|
||||
GET_SOURCE_PUBLIC_KEY,False,False
|
||||
GROUPING,False,False
|
||||
GROUPS,False,False
|
||||
GTID_ONLY,False,False
|
||||
HISTOGRAM,False,False
|
||||
HISTORY,False,False
|
||||
INACTIVE,False,False
|
||||
INITIAL,False,False
|
||||
INITIATE,False,False
|
||||
INTERSECT,False,False
|
||||
INVISIBLE,False,False
|
||||
JSON_TABLE,False,False
|
||||
JSON_VALUE,False,False
|
||||
KEYRING,False,False
|
||||
LAG,False,False
|
||||
LAST_VALUE,False,False
|
||||
LATERAL,False,False
|
||||
LEAD,False,False
|
||||
LOCKED,False,False
|
||||
MASTER_COMPRESSION_ALGORITHMS,False,False
|
||||
MASTER_PUBLIC_KEY_PATH,False,False
|
||||
MASTER_TLS_CIPHERSUITES,False,False
|
||||
MASTER_ZSTD_COMPRESSION_LEVEL,False,False
|
||||
MEMBER,False,False
|
||||
NESTED,False,False
|
||||
NETWORK_NAMESPACE,False,False
|
||||
NOWAIT,False,False
|
||||
NTH_VALUE,False,False
|
||||
NTILE,False,False
|
||||
NULLS,False,False
|
||||
OF,False,False
|
||||
OFF,False,False
|
||||
OJ,False,False
|
||||
OLD,False,False
|
||||
OPTIONAL,False,False
|
||||
ORDINALITY,False,False
|
||||
ORGANIZATION,False,False
|
||||
OTHERS,False,False
|
||||
OVER,False,False
|
||||
PASSWORD_LOCK_TIME,False,False
|
||||
PATH,False,False
|
||||
PERCENT_RANK,False,False
|
||||
PERSIST,False,False
|
||||
PERSIST_ONLY,False,False
|
||||
PRECEDING,False,False
|
||||
PRIVILEGE_CHECKS_USER,False,False
|
||||
PROCESS,False,False
|
||||
RANDOM,False,False
|
||||
RANK,False,False
|
||||
RECURSIVE,False,False
|
||||
REFERENCE,False,False
|
||||
REGISTRATION,False,False
|
||||
REPLICA,False,False
|
||||
REPLICAS,False,False
|
||||
REQUIRE_ROW_FORMAT,False,False
|
||||
RESOURCE,False,False
|
||||
RESPECT,False,False
|
||||
RESTART,False,False
|
||||
RETAIN,False,False
|
||||
RETURNING,False,False
|
||||
REUSE,False,False
|
||||
ROLE,False,False
|
||||
ROW_NUMBER,False,False
|
||||
SECONDARY,False,False
|
||||
SECONDARY_ENGINE,False,False
|
||||
SECONDARY_ENGINE_ATTRIBUTE,False,False
|
||||
SECONDARY_LOAD,False,False
|
||||
SECONDARY_UNLOAD,False,False
|
||||
SKIP,False,False
|
||||
SOURCE_AUTO_POSITION,False,False
|
||||
SOURCE_BIND,False,False
|
||||
SOURCE_COMPRESSION_ALGORITHMS,False,False
|
||||
SOURCE_CONNECT_RETRY,False,False
|
||||
SOURCE_DELAY,False,False
|
||||
SOURCE_HEARTBEAT_PERIOD,False,False
|
||||
SOURCE_HOST,False,False
|
||||
SOURCE_LOG_FILE,False,False
|
||||
SOURCE_LOG_POS,False,False
|
||||
SOURCE_PASSWORD,False,False
|
||||
SOURCE_PORT,False,False
|
||||
SOURCE_PUBLIC_KEY_PATH,False,False
|
||||
SOURCE_RETRY_COUNT,False,False
|
||||
SOURCE_SSL,False,False
|
||||
SOURCE_SSL_CA,False,False
|
||||
SOURCE_SSL_CAPATH,False,False
|
||||
SOURCE_SSL_CERT,False,False
|
||||
SOURCE_SSL_CIPHER,False,False
|
||||
SOURCE_SSL_CRL,False,False
|
||||
SOURCE_SSL_CRLPATH,False,False
|
||||
SOURCE_SSL_KEY,False,False
|
||||
SOURCE_SSL_VERIFY_SERVER_CERT,False,False
|
||||
SOURCE_TLS_CIPHERSUITES,False,False
|
||||
SOURCE_TLS_VERSION,False,False
|
||||
SOURCE_USER,False,False
|
||||
SOURCE_ZSTD_COMPRESSION_LEVEL,False,False
|
||||
SRID,False,False
|
||||
STREAM,False,False
|
||||
SYSTEM,False,False
|
||||
THREAD_PRIORITY,False,False
|
||||
TIES,False,False
|
||||
TLS,False,False
|
||||
UNBOUNDED,False,False
|
||||
UNREGISTER,False,False
|
||||
URL,False,False
|
||||
VCPU,False,False
|
||||
VISIBLE,False,False
|
||||
WINDOW,False,False
|
||||
ZONE,False,False
|
|
0
src/harlequin_mysql/py.typed
Normal file
0
src/harlequin_mysql/py.typed
Normal file
43
tests/conftest.py
Normal file
43
tests/conftest.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Generator
|
||||
|
||||
import pytest
|
||||
from mysql.connector import connect
|
||||
|
||||
from harlequin_mysql.adapter import (
|
||||
HarlequinMySQLAdapter,
|
||||
HarlequinMySQLConnection,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def connection() -> Generator[HarlequinMySQLConnection, None, None]:
|
||||
mysqlconn = connect(
|
||||
host="localhost",
|
||||
user="root",
|
||||
password="example",
|
||||
database="mysql",
|
||||
autocommit=True,
|
||||
)
|
||||
cur = mysqlconn.cursor()
|
||||
cur.execute("drop database if exists test;")
|
||||
cur.execute("drop database if exists one;")
|
||||
cur.execute("drop database if exists two;")
|
||||
cur.execute("drop database if exists three;")
|
||||
cur.execute("create database test;")
|
||||
cur.close()
|
||||
conn = HarlequinMySQLAdapter(
|
||||
conn_str=tuple(),
|
||||
host="localhost",
|
||||
user="root",
|
||||
password="example",
|
||||
database="test",
|
||||
).connect()
|
||||
yield conn
|
||||
cur = mysqlconn.cursor()
|
||||
cur.execute("drop database if exists test;")
|
||||
cur.execute("drop database if exists one;")
|
||||
cur.execute("drop database if exists two;")
|
||||
cur.execute("drop database if exists three;")
|
||||
cur.close()
|
220
tests/test_adapter.py
Normal file
220
tests/test_adapter.py
Normal file
|
@ -0,0 +1,220 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from harlequin import (
|
||||
HarlequinAdapter,
|
||||
HarlequinCompletion,
|
||||
HarlequinConnection,
|
||||
HarlequinCursor,
|
||||
)
|
||||
from harlequin.catalog import Catalog, CatalogItem
|
||||
from harlequin.exception import HarlequinConnectionError, HarlequinQueryError
|
||||
from mysql.connector.cursor import MySQLCursor
|
||||
from mysql.connector.pooling import PooledMySQLConnection
|
||||
from textual_fastdatatable.backend import create_backend
|
||||
|
||||
from harlequin_mysql.adapter import (
|
||||
HarlequinMySQLAdapter,
|
||||
HarlequinMySQLConnection,
|
||||
)
|
||||
|
||||
if sys.version_info < (3, 10):
|
||||
from importlib_metadata import entry_points
|
||||
else:
|
||||
from importlib.metadata import entry_points
|
||||
|
||||
|
||||
def test_plugin_discovery() -> None:
|
||||
PLUGIN_NAME = "mysql"
|
||||
eps = entry_points(group="harlequin.adapter")
|
||||
assert eps[PLUGIN_NAME]
|
||||
adapter_cls = eps[PLUGIN_NAME].load()
|
||||
assert issubclass(adapter_cls, HarlequinAdapter)
|
||||
assert adapter_cls == HarlequinMySQLAdapter
|
||||
|
||||
|
||||
def test_connect() -> None:
|
||||
conn = HarlequinMySQLAdapter(
|
||||
conn_str=tuple(), user="root", password="example"
|
||||
).connect()
|
||||
assert isinstance(conn, HarlequinConnection)
|
||||
|
||||
|
||||
def test_init_extra_kwargs() -> None:
|
||||
assert HarlequinMySQLAdapter(
|
||||
conn_str=tuple(), user="root", password="example", foo=1, bar="baz"
|
||||
).connect()
|
||||
|
||||
|
||||
def test_connect_raises_connection_error() -> None:
|
||||
with pytest.raises(HarlequinConnectionError):
|
||||
_ = HarlequinMySQLAdapter(conn_str=("foo",)).connect()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"options,expected",
|
||||
[
|
||||
({}, "127.0.0.1:3306/"),
|
||||
({"host": "foo.bar"}, "foo.bar:3306/"),
|
||||
({"host": "foo.bar", "port": "3305"}, "foo.bar:3305/"),
|
||||
({"unix_socket": "/foo/bar"}, "/foo/bar:3306/"),
|
||||
({"unix_socket": "/foo/bar", "database": "baz"}, "/foo/bar:3306/baz"),
|
||||
],
|
||||
)
|
||||
def test_connection_id(options: dict[str, str | int | None], expected: str) -> None:
|
||||
adapter = HarlequinMySQLAdapter(
|
||||
conn_str=tuple(),
|
||||
**options, # type: ignore[arg-type]
|
||||
)
|
||||
assert adapter.connection_id == expected
|
||||
|
||||
|
||||
def test_get_catalog(connection: HarlequinMySQLConnection) -> None:
|
||||
catalog = connection.get_catalog()
|
||||
assert isinstance(catalog, Catalog)
|
||||
assert catalog.items
|
||||
assert isinstance(catalog.items[0], CatalogItem)
|
||||
assert any(
|
||||
item.label == "test" and item.type_label == "db" for item in catalog.items
|
||||
)
|
||||
|
||||
|
||||
def test_get_completions(connection: HarlequinMySQLConnection) -> None:
|
||||
completions = connection.get_completions()
|
||||
assert completions
|
||||
assert isinstance(completions[0], HarlequinCompletion)
|
||||
expected = ["action", "var_pop"]
|
||||
filtered = list(filter(lambda x: x.label in expected, completions))
|
||||
assert len(filtered) == len(expected)
|
||||
|
||||
|
||||
def test_execute_ddl(connection: HarlequinMySQLConnection) -> None:
|
||||
cur = connection.execute("create table foo (a int)")
|
||||
assert cur is None
|
||||
|
||||
|
||||
def test_execute_select(connection: HarlequinMySQLConnection) -> None:
|
||||
cur = connection.execute("select 1 as a")
|
||||
assert isinstance(cur, HarlequinCursor)
|
||||
assert cur.columns() == [("a", "##")]
|
||||
data = cur.fetchall()
|
||||
backend = create_backend(data)
|
||||
assert backend.column_count == 1
|
||||
assert backend.row_count == 1
|
||||
|
||||
|
||||
def test_execute_select_no_records(connection: HarlequinMySQLConnection) -> None:
|
||||
cur = connection.execute("select 1 as a where false")
|
||||
assert isinstance(cur, HarlequinCursor)
|
||||
assert cur.columns() == [("a", "##")]
|
||||
data = cur.fetchall()
|
||||
backend = create_backend(data)
|
||||
assert backend.row_count == 0
|
||||
|
||||
|
||||
def test_execute_select_dupe_cols(connection: HarlequinMySQLConnection) -> None:
|
||||
cur = connection.execute("select 1 as a, 2 as a, 3 as a")
|
||||
assert isinstance(cur, HarlequinCursor)
|
||||
assert len(cur.columns()) == 3
|
||||
data = cur.fetchall()
|
||||
backend = create_backend(data)
|
||||
assert backend.column_count == 3
|
||||
assert backend.row_count == 1
|
||||
|
||||
|
||||
def test_set_limit(connection: HarlequinMySQLConnection) -> None:
|
||||
cur = connection.execute("select 1 as a union all select 2 union all select 3")
|
||||
assert isinstance(cur, HarlequinCursor)
|
||||
cur = cur.set_limit(2)
|
||||
assert isinstance(cur, HarlequinCursor)
|
||||
data = cur.fetchall()
|
||||
backend = create_backend(data)
|
||||
assert backend.column_count == 1
|
||||
assert backend.row_count == 2
|
||||
|
||||
|
||||
def test_execute_raises_query_error(connection: HarlequinMySQLConnection) -> None:
|
||||
with pytest.raises(HarlequinQueryError):
|
||||
_ = connection.execute("selec;")
|
||||
|
||||
|
||||
def test_can_execute_pool_size_queries(connection: HarlequinMySQLConnection) -> None:
|
||||
pool_size = connection._pool.pool_size
|
||||
cursors: list[HarlequinCursor] = []
|
||||
for _ in range(pool_size):
|
||||
cur = connection.execute("select 1")
|
||||
assert cur is not None
|
||||
cursors.append(cur)
|
||||
assert len(cursors) == pool_size
|
||||
|
||||
|
||||
def test_can_execute_pool_size_ddl(connection: HarlequinMySQLConnection) -> None:
|
||||
pool_size = connection._pool.pool_size
|
||||
cursors: list[None] = []
|
||||
for i in range(pool_size):
|
||||
cur = connection.execute(f"create table t_{i} as select {i}")
|
||||
assert cur is None
|
||||
cursors.append(cur)
|
||||
assert len(cursors) == pool_size
|
||||
|
||||
|
||||
def test_execute_more_than_pool_size_queries_does_not_raise(
|
||||
connection: HarlequinMySQLConnection,
|
||||
) -> None:
|
||||
pool_size = connection._pool.pool_size
|
||||
cursors: list[HarlequinCursor] = []
|
||||
for _ in range(pool_size * 2):
|
||||
cur = connection.execute("select 1")
|
||||
if cur is not None:
|
||||
cursors.append(cur)
|
||||
assert len(cursors) == pool_size
|
||||
|
||||
|
||||
def test_execute_more_than_pool_size_ddl_does_not_raise(
|
||||
connection: HarlequinMySQLConnection,
|
||||
) -> None:
|
||||
pool_size = connection._pool.pool_size
|
||||
number_of_ddl_queries = pool_size * 2
|
||||
cursors: list[None] = []
|
||||
for i in range(number_of_ddl_queries):
|
||||
cur = connection.execute(f"create table t_{i} as select {i}")
|
||||
assert cur is None
|
||||
cursors.append(cur)
|
||||
assert len(cursors) == number_of_ddl_queries
|
||||
|
||||
|
||||
def test_use_database_updates_pool(connection: HarlequinMySQLConnection) -> None:
|
||||
conn, cur = connection.safe_get_mysql_cursor()
|
||||
assert conn is not None
|
||||
assert cur is not None
|
||||
assert conn.database == "test"
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
connection.execute("use mysql")
|
||||
|
||||
pool_size = connection._pool.pool_size
|
||||
|
||||
conns: list[PooledMySQLConnection] = []
|
||||
curs: list[MySQLCursor] = []
|
||||
for _ in range(pool_size):
|
||||
conn, cur = connection.safe_get_mysql_cursor()
|
||||
assert conn is not None
|
||||
assert cur is not None
|
||||
assert conn.database == "mysql"
|
||||
conns.append(conn)
|
||||
curs.append(cur)
|
||||
|
||||
assert len(conns) == pool_size
|
||||
for cur in curs:
|
||||
cur.close()
|
||||
for conn in conns:
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_close(connection: HarlequinMySQLConnection) -> None:
|
||||
connection.close()
|
||||
# run again to test error handling.
|
||||
connection.close()
|
85
tests/test_catalog.py
Normal file
85
tests/test_catalog.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
import pytest
|
||||
|
||||
from harlequin_mysql.adapter import HarlequinMySQLConnection
|
||||
from harlequin_mysql.catalog import (
|
||||
ColumnCatalogItem,
|
||||
DatabaseCatalogItem,
|
||||
RelationCatalogItem,
|
||||
TableCatalogItem,
|
||||
ViewCatalogItem,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def connection_with_objects(
|
||||
connection: HarlequinMySQLConnection,
|
||||
) -> HarlequinMySQLConnection:
|
||||
connection.execute("create database one")
|
||||
connection.execute("create table one.foo as select 1 as a, '2' as b")
|
||||
connection.execute("create table one.bar as select 1 as a, '2' as b")
|
||||
connection.execute("create table one.baz as select 1 as a, '2' as b")
|
||||
connection.execute("create database two")
|
||||
connection.execute("create view two.qux as select * from one.foo")
|
||||
connection.execute("create database three")
|
||||
# the original connection fixture will clean this up.
|
||||
return connection
|
||||
|
||||
|
||||
def test_catalog(connection_with_objects: HarlequinMySQLConnection) -> None:
|
||||
conn = connection_with_objects
|
||||
|
||||
catalog = conn.get_catalog()
|
||||
|
||||
# five databases: dev, test, one, two, and three.
|
||||
assert len(catalog.items) == 5
|
||||
|
||||
database_items = catalog.items
|
||||
assert all(isinstance(item, DatabaseCatalogItem) for item in database_items)
|
||||
|
||||
[database_one_item] = filter(lambda item: item.label == "one", database_items)
|
||||
assert isinstance(database_one_item, DatabaseCatalogItem)
|
||||
assert not database_one_item.children
|
||||
assert not database_one_item.loaded
|
||||
|
||||
table_items = database_one_item.fetch_children()
|
||||
assert all(isinstance(item, RelationCatalogItem) for item in table_items)
|
||||
|
||||
[foo_item] = filter(lambda item: item.label == "foo", table_items)
|
||||
assert isinstance(foo_item, TableCatalogItem)
|
||||
assert not foo_item.children
|
||||
assert not foo_item.loaded
|
||||
|
||||
foo_column_items = foo_item.fetch_children()
|
||||
assert all(isinstance(item, ColumnCatalogItem) for item in foo_column_items)
|
||||
|
||||
[database_two_item] = filter(lambda item: item.label == "two", database_items)
|
||||
assert isinstance(database_two_item, DatabaseCatalogItem)
|
||||
assert not database_two_item.children
|
||||
assert not database_two_item.loaded
|
||||
|
||||
view_items = database_two_item.fetch_children()
|
||||
assert all(isinstance(item, ViewCatalogItem) for item in view_items)
|
||||
|
||||
[qux_item] = filter(lambda item: item.label == "qux", view_items)
|
||||
assert isinstance(qux_item, ViewCatalogItem)
|
||||
assert not qux_item.children
|
||||
assert not qux_item.loaded
|
||||
|
||||
qux_column_items = qux_item.fetch_children()
|
||||
assert all(isinstance(item, ColumnCatalogItem) for item in qux_column_items)
|
||||
|
||||
assert [item.label for item in foo_column_items] == [
|
||||
item.label for item in qux_column_items
|
||||
]
|
||||
|
||||
# ensure calling fetch_children on cols doesn't raise
|
||||
children_items = foo_column_items[0].fetch_children()
|
||||
assert not children_items
|
||||
|
||||
[database_three_item] = filter(lambda item: item.label == "three", database_items)
|
||||
assert isinstance(database_three_item, DatabaseCatalogItem)
|
||||
assert not database_three_item.children
|
||||
assert not database_three_item.loaded
|
||||
|
||||
three_children = database_three_item.fetch_children()
|
||||
assert not three_children
|
Loading…
Add table
Add a link
Reference in a new issue