david8-duckdb 0.1.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- david8_duckdb-0.1.0b1/PKG-INFO +29 -0
- david8_duckdb-0.1.0b1/README.md +5 -0
- david8_duckdb-0.1.0b1/david8_duckdb/__init__.py +10 -0
- david8_duckdb-0.1.0b1/david8_duckdb/core/__init__.py +0 -0
- david8_duckdb-0.1.0b1/david8_duckdb/core/query_builder.py +11 -0
- david8_duckdb-0.1.0b1/david8_duckdb/core/select_query.py +49 -0
- david8_duckdb-0.1.0b1/david8_duckdb/protocols/__init__.py +0 -0
- david8_duckdb-0.1.0b1/david8_duckdb/protocols/query_builder.py +9 -0
- david8_duckdb-0.1.0b1/david8_duckdb/protocols/sql.py +38 -0
- david8_duckdb-0.1.0b1/david8_duckdb.egg-info/PKG-INFO +29 -0
- david8_duckdb-0.1.0b1/david8_duckdb.egg-info/SOURCES.txt +15 -0
- david8_duckdb-0.1.0b1/david8_duckdb.egg-info/dependency_links.txt +1 -0
- david8_duckdb-0.1.0b1/david8_duckdb.egg-info/requires.txt +1 -0
- david8_duckdb-0.1.0b1/david8_duckdb.egg-info/top_level.txt +1 -0
- david8_duckdb-0.1.0b1/pyproject.toml +83 -0
- david8_duckdb-0.1.0b1/setup.cfg +4 -0
- david8_duckdb-0.1.0b1/tests/test_select.py +93 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: david8_duckdb
|
|
3
|
+
Version: 0.1.0b1
|
|
4
|
+
Summary: SQL query builder. david8 duckdb dielect
|
|
5
|
+
Author-email: Danila Ganchar <danila.ganchar@gmail.com>
|
|
6
|
+
Maintainer-email: Danila Ganchar <danila.ganchar@gmail.com>
|
|
7
|
+
Project-URL: Homepage, https://github.com/d-ganchar/david8_duckdb
|
|
8
|
+
Project-URL: Changelog, https://github.com/d-ganchar/david8_duckdb/releases
|
|
9
|
+
Project-URL: Issues, https://github.com/d-ganchar/david8_duckdb/issues
|
|
10
|
+
Project-URL: CI, https://github.com/d-ganchar/david8_duckdb/actions
|
|
11
|
+
Project-URL: Documentation, https://github.com/d-ganchar/david8_duckdb/wiki
|
|
12
|
+
Project-URL: Source, https://github.com/d-ganchar/david8_duckdb
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Classifier: Topic :: Database
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Requires-Python: <3.14,>=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: david8<2.0.0b1,>=1.0.0b1
|
|
24
|
+
|
|
25
|
+
# david8_duckdb
|
|
26
|
+
|
|
27
|
+
`david8_duckdb` is [DuckDb](https://duckdb.org/) dialect for [david8](https://github.com/d-ganchar/david8)
|
|
28
|
+
|
|
29
|
+
See [Wiki](https://github.com/d-ganchar/david8_duckdb/wiki)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from david8.core.base_dialect import BaseDialect as _BaseDialect
|
|
2
|
+
from david8.param_styles import QMarkParamStyle
|
|
3
|
+
|
|
4
|
+
from .core.query_builder import DuckDbQueryBuilder as _QueryBuilder
|
|
5
|
+
from .protocols.query_builder import QueryBuilderProtocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_qb(is_quote_mode: bool = False) -> QueryBuilderProtocol:
|
|
9
|
+
dialect = _BaseDialect(QMarkParamStyle(), is_quote_mode)
|
|
10
|
+
return _QueryBuilder(dialect)
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from david8.core.base_query_builder import BaseQueryBuilder as _BaseQueryBuilder
|
|
2
|
+
from david8.protocols.sql import AliasedProtocol, ExprProtocol, FunctionProtocol
|
|
3
|
+
|
|
4
|
+
from ..protocols.query_builder import QueryBuilderProtocol
|
|
5
|
+
from ..protocols.sql import SelectProtocol
|
|
6
|
+
from .select_query import DuckDbSelect
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DuckDbQueryBuilder(QueryBuilderProtocol, _BaseQueryBuilder):
|
|
10
|
+
def select(self, *args: str | AliasedProtocol | ExprProtocol | FunctionProtocol) -> SelectProtocol:
|
|
11
|
+
return DuckDbSelect(select_columns=args, dialect=self._dialect)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
|
|
3
|
+
from david8.core.base_dql import BaseSelect as _BaseSelect
|
|
4
|
+
from david8.protocols.dialect import DialectProtocol
|
|
5
|
+
|
|
6
|
+
from ..protocols.sql import SelectProtocol
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DuckDbSelect(_BaseSelect, SelectProtocol):
|
|
10
|
+
from_files: tuple[str, tuple[str, ...]] = dataclasses.field(default_factory=tuple)
|
|
11
|
+
|
|
12
|
+
def from_csv(self, *file_names: str) -> 'SelectProtocol':
|
|
13
|
+
self.from_files = ('read_csv', file_names)
|
|
14
|
+
return self
|
|
15
|
+
|
|
16
|
+
def from_json(self, *file_names: str) -> 'SelectProtocol':
|
|
17
|
+
self.from_files = ('read_json', file_names)
|
|
18
|
+
return self
|
|
19
|
+
|
|
20
|
+
def from_json_objects(self, *file_names: str) -> 'SelectProtocol':
|
|
21
|
+
self.from_files = ('read_json_objects', file_names)
|
|
22
|
+
return self
|
|
23
|
+
|
|
24
|
+
def from_ndjson_objects(self, *file_names: str) -> 'SelectProtocol':
|
|
25
|
+
self.from_files = ('read_ndjson_objects', file_names)
|
|
26
|
+
return self
|
|
27
|
+
|
|
28
|
+
def from_json_objects_auto(self, *file_names: str) -> 'SelectProtocol':
|
|
29
|
+
self.from_files = ('read_json_objects_auto', file_names)
|
|
30
|
+
return self
|
|
31
|
+
|
|
32
|
+
def from_parquet(self, *file_names: str) -> 'SelectProtocol':
|
|
33
|
+
self.from_files = ('read_parquet', file_names)
|
|
34
|
+
return self
|
|
35
|
+
|
|
36
|
+
def from_xlsx(self, file_name: str) -> 'SelectProtocol':
|
|
37
|
+
self.from_files = ('read_xlsx', (file_name,))
|
|
38
|
+
return self
|
|
39
|
+
|
|
40
|
+
def _from_to_sql(self, dialect: DialectProtocol) -> str:
|
|
41
|
+
if not self.from_files:
|
|
42
|
+
return super()._from_to_sql(dialect)
|
|
43
|
+
|
|
44
|
+
if len(self.from_files[1]) > 1:
|
|
45
|
+
files = str(list(self.from_files[1]))
|
|
46
|
+
else:
|
|
47
|
+
files = f"'{self.from_files[1][0]}'"
|
|
48
|
+
|
|
49
|
+
return f' FROM {self.from_files[0]}({files})'
|
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from david8.protocols.query_builder import QueryBuilderProtocol as _QueryBuilderProtocol
|
|
2
|
+
from david8.protocols.sql import AliasedProtocol, ExprProtocol, FunctionProtocol
|
|
3
|
+
|
|
4
|
+
from ..protocols.sql import SelectProtocol
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class QueryBuilderProtocol(_QueryBuilderProtocol):
|
|
8
|
+
def select(self, *args: str | AliasedProtocol | ExprProtocol | FunctionProtocol) -> SelectProtocol:
|
|
9
|
+
pass
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from david8.protocols.sql import SelectProtocol as _SelectProtocol
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SelectProtocol(_SelectProtocol):
|
|
5
|
+
def from_csv(self, *file_names: str) -> 'SelectProtocol':
|
|
6
|
+
"""
|
|
7
|
+
https://duckdb.org/docs/stable/data/csv/reading_faulty_csv_files
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def from_json(self, *file_names: str) -> 'SelectProtocol':
|
|
11
|
+
"""
|
|
12
|
+
https://duckdb.org/docs/stable/data/json/loading_json#the-read_json-function
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def from_json_objects(self, *file_names: str) -> 'SelectProtocol':
|
|
16
|
+
"""
|
|
17
|
+
https://duckdb.org/docs/stable/data/json/loading_json#functions-for-reading-json-objects
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def from_ndjson_objects(self, *file_names: str) -> 'SelectProtocol':
|
|
21
|
+
"""
|
|
22
|
+
https://duckdb.org/docs/stable/data/json/loading_json#functions-for-reading-json-objects
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def from_json_objects_auto(self, *file_names: str) -> 'SelectProtocol':
|
|
26
|
+
"""
|
|
27
|
+
https://duckdb.org/docs/stable/data/json/loading_json#functions-for-reading-json-objects
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def from_parquet(self, *file_names: str) -> 'SelectProtocol':
|
|
31
|
+
"""
|
|
32
|
+
read_parquet(): https://duckdb.org/docs/stable/data/parquet/overview
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def from_xlsx(self, file_name: str) -> 'SelectProtocol':
|
|
36
|
+
"""
|
|
37
|
+
read_xlsx(): https://duckdb.org/docs/stable/core_extensions/excel
|
|
38
|
+
"""
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: david8_duckdb
|
|
3
|
+
Version: 0.1.0b1
|
|
4
|
+
Summary: SQL query builder. david8 duckdb dielect
|
|
5
|
+
Author-email: Danila Ganchar <danila.ganchar@gmail.com>
|
|
6
|
+
Maintainer-email: Danila Ganchar <danila.ganchar@gmail.com>
|
|
7
|
+
Project-URL: Homepage, https://github.com/d-ganchar/david8_duckdb
|
|
8
|
+
Project-URL: Changelog, https://github.com/d-ganchar/david8_duckdb/releases
|
|
9
|
+
Project-URL: Issues, https://github.com/d-ganchar/david8_duckdb/issues
|
|
10
|
+
Project-URL: CI, https://github.com/d-ganchar/david8_duckdb/actions
|
|
11
|
+
Project-URL: Documentation, https://github.com/d-ganchar/david8_duckdb/wiki
|
|
12
|
+
Project-URL: Source, https://github.com/d-ganchar/david8_duckdb
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Classifier: Topic :: Database
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Requires-Python: <3.14,>=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: david8<2.0.0b1,>=1.0.0b1
|
|
24
|
+
|
|
25
|
+
# david8_duckdb
|
|
26
|
+
|
|
27
|
+
`david8_duckdb` is [DuckDb](https://duckdb.org/) dialect for [david8](https://github.com/d-ganchar/david8)
|
|
28
|
+
|
|
29
|
+
See [Wiki](https://github.com/d-ganchar/david8_duckdb/wiki)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
david8_duckdb/__init__.py
|
|
4
|
+
david8_duckdb.egg-info/PKG-INFO
|
|
5
|
+
david8_duckdb.egg-info/SOURCES.txt
|
|
6
|
+
david8_duckdb.egg-info/dependency_links.txt
|
|
7
|
+
david8_duckdb.egg-info/requires.txt
|
|
8
|
+
david8_duckdb.egg-info/top_level.txt
|
|
9
|
+
david8_duckdb/core/__init__.py
|
|
10
|
+
david8_duckdb/core/query_builder.py
|
|
11
|
+
david8_duckdb/core/select_query.py
|
|
12
|
+
david8_duckdb/protocols/__init__.py
|
|
13
|
+
david8_duckdb/protocols/query_builder.py
|
|
14
|
+
david8_duckdb/protocols/sql.py
|
|
15
|
+
tests/test_select.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
david8<2.0.0b1,>=1.0.0b1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
david8_duckdb
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "david8_duckdb"
|
|
3
|
+
version = "0.1.0b1"
|
|
4
|
+
description = "SQL query builder. david8 duckdb dielect"
|
|
5
|
+
authors = [{name = "Danila Ganchar", email = "danila.ganchar@gmail.com"}]
|
|
6
|
+
maintainers = [{name = "Danila Ganchar", email = "danila.ganchar@gmail.com"}]
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.11, < 3.14"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"david8>=1.0.0b1,<2.0.0b1"
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Topic :: Software Development :: Libraries",
|
|
18
|
+
"Topic :: Database",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/d-ganchar/david8_duckdb"
|
|
28
|
+
Changelog = "https://github.com/d-ganchar/david8_duckdb/releases"
|
|
29
|
+
Issues = "https://github.com/d-ganchar/david8_duckdb/issues"
|
|
30
|
+
CI = "https://github.com/d-ganchar/david8_duckdb/actions"
|
|
31
|
+
Documentation = "https://github.com/d-ganchar/david8_duckdb/wiki"
|
|
32
|
+
Source = "https://github.com/d-ganchar/david8_duckdb"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
[build-system]
|
|
36
|
+
requires = ["setuptools>=77.0.0"]
|
|
37
|
+
build-backend = "setuptools.build_meta"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages]
|
|
41
|
+
find = { include = ["david8_duckdb", "david8_duckdb.*"] }
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
[dependency-groups]
|
|
45
|
+
dev = [
|
|
46
|
+
"ruff",
|
|
47
|
+
"parameterized",
|
|
48
|
+
"pytest",
|
|
49
|
+
"pytest-cov>=7.0.0",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[lint.select]
|
|
53
|
+
|
|
54
|
+
[lint.isort]
|
|
55
|
+
known-third-party = ["pytest"]
|
|
56
|
+
lines-after-imports = 2
|
|
57
|
+
lines-between-types = 1
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
line-length = 120
|
|
62
|
+
exclude = [
|
|
63
|
+
".bzr",
|
|
64
|
+
".direnv",
|
|
65
|
+
".eggs",
|
|
66
|
+
"**/.git",
|
|
67
|
+
"__pycache__",
|
|
68
|
+
"tests/fixtures/",
|
|
69
|
+
"benchmarks/"
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint]
|
|
73
|
+
select = ["E", "F", "I", "UP", "N", "B", "W"]
|
|
74
|
+
|
|
75
|
+
[tool.pytest.ini_options]
|
|
76
|
+
testpaths = [
|
|
77
|
+
"tests",
|
|
78
|
+
"--color=yes",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
addopts = [
|
|
82
|
+
|
|
83
|
+
]
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from parameterized import parameterized
|
|
2
|
+
|
|
3
|
+
from david8_duckdb.protocols.sql import SelectProtocol
|
|
4
|
+
from tests.base_test import BaseTest
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestSelect(BaseTest):
|
|
8
|
+
@parameterized.expand([
|
|
9
|
+
(
|
|
10
|
+
BaseTest.qb.select('*').from_csv('faulty.csv'),
|
|
11
|
+
"SELECT * FROM read_csv('faulty.csv')",
|
|
12
|
+
),
|
|
13
|
+
(
|
|
14
|
+
BaseTest.qb.select('*').from_csv('flights1.csv', 'flights2.csv'),
|
|
15
|
+
"SELECT * FROM read_csv(['flights1.csv', 'flights2.csv'])",
|
|
16
|
+
),
|
|
17
|
+
])
|
|
18
|
+
def test_from_csv(self, query: SelectProtocol, exp_sql: str):
|
|
19
|
+
self.assertEqual(query.get_sql(), exp_sql)
|
|
20
|
+
|
|
21
|
+
@parameterized.expand([
|
|
22
|
+
(
|
|
23
|
+
BaseTest.qb.select('*').from_json('todos.json'),
|
|
24
|
+
"SELECT * FROM read_json('todos.json')",
|
|
25
|
+
),
|
|
26
|
+
(
|
|
27
|
+
BaseTest.qb.select('*').from_json('todos1.json', 'todos2.json'),
|
|
28
|
+
"SELECT * FROM read_json(['todos1.json', 'todos2.json'])",
|
|
29
|
+
),
|
|
30
|
+
])
|
|
31
|
+
def test_from_json(self, query: SelectProtocol, exp_sql: str):
|
|
32
|
+
self.assertEqual(query.get_sql(), exp_sql)
|
|
33
|
+
|
|
34
|
+
@parameterized.expand([
|
|
35
|
+
(
|
|
36
|
+
BaseTest.qb.select('*').from_json_objects('todos.json'),
|
|
37
|
+
"SELECT * FROM read_json_objects('todos.json')",
|
|
38
|
+
),
|
|
39
|
+
(
|
|
40
|
+
BaseTest.qb.select('*').from_json_objects('todos1.json', 'todos2.json'),
|
|
41
|
+
"SELECT * FROM read_json_objects(['todos1.json', 'todos2.json'])",
|
|
42
|
+
),
|
|
43
|
+
])
|
|
44
|
+
def test_from_json_objects(self, query: SelectProtocol, exp_sql: str):
|
|
45
|
+
self.assertEqual(query.get_sql(), exp_sql)
|
|
46
|
+
|
|
47
|
+
@parameterized.expand([
|
|
48
|
+
(
|
|
49
|
+
BaseTest.qb.select('*').from_ndjson_objects('todos.json'),
|
|
50
|
+
"SELECT * FROM read_ndjson_objects('todos.json')",
|
|
51
|
+
),
|
|
52
|
+
(
|
|
53
|
+
BaseTest.qb.select('*').from_ndjson_objects('todos1.json', 'todos2.json'),
|
|
54
|
+
"SELECT * FROM read_ndjson_objects(['todos1.json', 'todos2.json'])",
|
|
55
|
+
),
|
|
56
|
+
])
|
|
57
|
+
def test_from_ndjson_objects(self, query: SelectProtocol, exp_sql: str):
|
|
58
|
+
self.assertEqual(query.get_sql(), exp_sql)
|
|
59
|
+
|
|
60
|
+
@parameterized.expand([
|
|
61
|
+
(
|
|
62
|
+
BaseTest.qb.select('*').from_json_objects_auto('todos.json'),
|
|
63
|
+
"SELECT * FROM read_json_objects_auto('todos.json')",
|
|
64
|
+
),
|
|
65
|
+
(
|
|
66
|
+
BaseTest.qb.select('*').from_json_objects_auto('todos1.json', 'todos2.json'),
|
|
67
|
+
"SELECT * FROM read_json_objects_auto(['todos1.json', 'todos2.json'])",
|
|
68
|
+
),
|
|
69
|
+
])
|
|
70
|
+
def test_from_json_objects_auto(self, query: SelectProtocol, exp_sql: str):
|
|
71
|
+
self.assertEqual(query.get_sql(), exp_sql)
|
|
72
|
+
|
|
73
|
+
@parameterized.expand([
|
|
74
|
+
(
|
|
75
|
+
BaseTest.qb.select('*').from_parquet('file.parquet'),
|
|
76
|
+
"SELECT * FROM read_parquet('file.parquet')",
|
|
77
|
+
),
|
|
78
|
+
(
|
|
79
|
+
BaseTest.qb.select('*').from_parquet('file1.parquet', 'file2.parquet'),
|
|
80
|
+
"SELECT * FROM read_parquet(['file1.parquet', 'file2.parquet'])",
|
|
81
|
+
),
|
|
82
|
+
])
|
|
83
|
+
def test_from_parquet(self, query: SelectProtocol, exp_sql: str):
|
|
84
|
+
self.assertEqual(query.get_sql(), exp_sql)
|
|
85
|
+
|
|
86
|
+
@parameterized.expand([
|
|
87
|
+
(
|
|
88
|
+
BaseTest.qb.select('*').from_xlsx('test.xlsx'),
|
|
89
|
+
"SELECT * FROM read_xlsx('test.xlsx')",
|
|
90
|
+
),
|
|
91
|
+
])
|
|
92
|
+
def test_from_xlsx(self, query: SelectProtocol, exp_sql: str):
|
|
93
|
+
self.assertEqual(query.get_sql(), exp_sql)
|