pgpack-dumper 0.1.2__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.
- pgpack_dumper-0.1.2/CHANGELOG.md +32 -0
- pgpack_dumper-0.1.2/MANIFEST.in +1 -0
- pgpack_dumper-0.1.2/PKG-INFO +139 -0
- pgpack_dumper-0.1.2/README.md +125 -0
- pgpack_dumper-0.1.2/pgpack_dumper/__init__.py +35 -0
- pgpack_dumper-0.1.2/pgpack_dumper/connector.py +11 -0
- pgpack_dumper-0.1.2/pgpack_dumper/copy.py +166 -0
- pgpack_dumper-0.1.2/pgpack_dumper/dumper.py +169 -0
- pgpack_dumper-0.1.2/pgpack_dumper/errors.py +14 -0
- pgpack_dumper-0.1.2/pgpack_dumper/logger.py +67 -0
- pgpack_dumper-0.1.2/pgpack_dumper/metadata.py +36 -0
- pgpack_dumper-0.1.2/pgpack_dumper/multiquery.py +22 -0
- pgpack_dumper-0.1.2/pgpack_dumper/query_path.py +7 -0
- pgpack_dumper-0.1.2/pgpack_dumper/query_template.py +10 -0
- pgpack_dumper-0.1.2/pgpack_dumper/queryes/attributes.sql +4 -0
- pgpack_dumper-0.1.2/pgpack_dumper/queryes/copy_from.sql +1 -0
- pgpack_dumper-0.1.2/pgpack_dumper/queryes/copy_to.sql +1 -0
- pgpack_dumper-0.1.2/pgpack_dumper/queryes/prepare.sql +4 -0
- pgpack_dumper-0.1.2/pgpack_dumper/queryes/relkind.sql +1 -0
- pgpack_dumper-0.1.2/pgpack_dumper/random_name.py +7 -0
- pgpack_dumper-0.1.2/pgpack_dumper/search_object.py +13 -0
- pgpack_dumper-0.1.2/pgpack_dumper/structs.py +34 -0
- pgpack_dumper-0.1.2/pgpack_dumper/version.py +1 -0
- pgpack_dumper-0.1.2/pgpack_dumper.egg-info/PKG-INFO +139 -0
- pgpack_dumper-0.1.2/pgpack_dumper.egg-info/SOURCES.txt +32 -0
- pgpack_dumper-0.1.2/pgpack_dumper.egg-info/dependency_links.txt +1 -0
- pgpack_dumper-0.1.2/pgpack_dumper.egg-info/not-zip-safe +1 -0
- pgpack_dumper-0.1.2/pgpack_dumper.egg-info/requires.txt +3 -0
- pgpack_dumper-0.1.2/pgpack_dumper.egg-info/top_level.txt +1 -0
- pgpack_dumper-0.1.2/pyproject.toml +3 -0
- pgpack_dumper-0.1.2/requirements.txt +3 -0
- pgpack_dumper-0.1.2/setup.cfg +14 -0
- pgpack_dumper-0.1.2/setup.py +31 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Version History
|
|
2
|
+
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
* Change metadata structure
|
|
6
|
+
* Update requirements.txt
|
|
7
|
+
|
|
8
|
+
## 0.1.1
|
|
9
|
+
|
|
10
|
+
* Rename project to pgpack_dumper
|
|
11
|
+
* Fix legacy setup.py bdist_wheel mechanism, which will be removed in a future version
|
|
12
|
+
* Fix multiquery
|
|
13
|
+
* Add CHANGELOG.md
|
|
14
|
+
|
|
15
|
+
## 0.1.0
|
|
16
|
+
|
|
17
|
+
* Add CopyBufferObjectError & CopyBufferTableNotDefined
|
|
18
|
+
* Add PGObject
|
|
19
|
+
* Add logger
|
|
20
|
+
* Add sqlparse for cut comments from query
|
|
21
|
+
* Add multiquery
|
|
22
|
+
* Update requirements.txt
|
|
23
|
+
|
|
24
|
+
## 0.0.2
|
|
25
|
+
|
|
26
|
+
* Fix include *.sql
|
|
27
|
+
* Fix requirements.txt
|
|
28
|
+
* Docs change README.md
|
|
29
|
+
|
|
30
|
+
## 0.0.1
|
|
31
|
+
|
|
32
|
+
First version of the library pgcrypt_dumper
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
recursive-include pgpack_dumper *.sql
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pgpack_dumper
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Library for read and write PGPack format between PostgreSQL and file.
|
|
5
|
+
Home-page: https://github.com/0xMihalich/pgpack_dumper
|
|
6
|
+
Author: 0xMihalich
|
|
7
|
+
Author-email: bayanmobile87@gmail.com
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: README.md
|
|
10
|
+
License-File: CHANGELOG.md
|
|
11
|
+
Requires-Dist: pgpack==0.1.3
|
|
12
|
+
Requires-Dist: psycopg>=3.2.9
|
|
13
|
+
Requires-Dist: sqlparse>=0.5.3
|
|
14
|
+
|
|
15
|
+
# PGPackDumper
|
|
16
|
+
|
|
17
|
+
Library for read and write PGPack format between PostgreSQL and file
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### Initialization
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from pgpack_dumper import (
|
|
25
|
+
CompressionMethod,
|
|
26
|
+
PGConnector,
|
|
27
|
+
PGPackDumper,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
connector = PGConnector(
|
|
31
|
+
host = <your host>,
|
|
32
|
+
dbname = <your database>,
|
|
33
|
+
user = <your username>,
|
|
34
|
+
password = <your password>,
|
|
35
|
+
port = <your port>,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
dumper = PGPackDumper(
|
|
39
|
+
connector=connector,
|
|
40
|
+
compression_method=CompressionMethod.LZ4, # or CompressionMethod.ZSTD or CompressionMethod.NONE
|
|
41
|
+
)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Read dump from PostgreSQL into file
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
file_name = "pgpack.lz4"
|
|
48
|
+
# you need define one of parameter query or table_name
|
|
49
|
+
query = "select ..." # some sql query
|
|
50
|
+
table_name = "public.test_table" # or some table
|
|
51
|
+
|
|
52
|
+
with open(file_name, "wb") as fileobj:
|
|
53
|
+
dumper.read_dump(
|
|
54
|
+
fileobj,
|
|
55
|
+
query,
|
|
56
|
+
table_name,
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Write dump from file into PostgreSQL
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
file_name = "pgpack.lz4"
|
|
64
|
+
# you need define one of parameter table_name
|
|
65
|
+
table_name = "public.test_table" # some table
|
|
66
|
+
|
|
67
|
+
with open(file_name, "rb") as fileobj:
|
|
68
|
+
dumper.write_dump(
|
|
69
|
+
fileobj,
|
|
70
|
+
table_name,
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Write from PostgreSQL into PostgreSQL
|
|
75
|
+
|
|
76
|
+
Same server
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
|
|
80
|
+
table_dest = "public.test_table_write" # some table for write
|
|
81
|
+
table_src = "public.test_table_read" # some table for read
|
|
82
|
+
query_src = "select ..." # or some sql query for read
|
|
83
|
+
|
|
84
|
+
dumper.write_between(
|
|
85
|
+
table_dest,
|
|
86
|
+
table_src,
|
|
87
|
+
query_src,
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Different servers
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
|
|
95
|
+
connector_src = PGConnector(
|
|
96
|
+
host = <host src>,
|
|
97
|
+
dbname = <database src>,
|
|
98
|
+
user = <username src>,
|
|
99
|
+
password = <password src>,
|
|
100
|
+
port = <port src>,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
dumper_src = PGPackDumper(connector=connector_src)
|
|
104
|
+
|
|
105
|
+
table_dest = "public.test_table_write" # some table for write
|
|
106
|
+
table_src = "public.test_table_read" # some table for read
|
|
107
|
+
query_src = "select ..." # or some sql query for read
|
|
108
|
+
|
|
109
|
+
dumper.write_between(
|
|
110
|
+
table_dest,
|
|
111
|
+
table_src,
|
|
112
|
+
query_src,
|
|
113
|
+
dumper_src.cursor,
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Open PGPack file format
|
|
118
|
+
|
|
119
|
+
Get info from my another repository https://github.com/0xMihalich/pgpack
|
|
120
|
+
|
|
121
|
+
## Installation
|
|
122
|
+
|
|
123
|
+
### From pip
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pip install pgpack_dumper
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### From local directory
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pip install .
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### From git
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pip install git+https://github.com/0xMihalich/pgpack_dumper
|
|
139
|
+
```
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# PGPackDumper
|
|
2
|
+
|
|
3
|
+
Library for read and write PGPack format between PostgreSQL and file
|
|
4
|
+
|
|
5
|
+
## Examples
|
|
6
|
+
|
|
7
|
+
### Initialization
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from pgpack_dumper import (
|
|
11
|
+
CompressionMethod,
|
|
12
|
+
PGConnector,
|
|
13
|
+
PGPackDumper,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
connector = PGConnector(
|
|
17
|
+
host = <your host>,
|
|
18
|
+
dbname = <your database>,
|
|
19
|
+
user = <your username>,
|
|
20
|
+
password = <your password>,
|
|
21
|
+
port = <your port>,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
dumper = PGPackDumper(
|
|
25
|
+
connector=connector,
|
|
26
|
+
compression_method=CompressionMethod.LZ4, # or CompressionMethod.ZSTD or CompressionMethod.NONE
|
|
27
|
+
)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Read dump from PostgreSQL into file
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
file_name = "pgpack.lz4"
|
|
34
|
+
# you need define one of parameter query or table_name
|
|
35
|
+
query = "select ..." # some sql query
|
|
36
|
+
table_name = "public.test_table" # or some table
|
|
37
|
+
|
|
38
|
+
with open(file_name, "wb") as fileobj:
|
|
39
|
+
dumper.read_dump(
|
|
40
|
+
fileobj,
|
|
41
|
+
query,
|
|
42
|
+
table_name,
|
|
43
|
+
)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Write dump from file into PostgreSQL
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
file_name = "pgpack.lz4"
|
|
50
|
+
# you need define one of parameter table_name
|
|
51
|
+
table_name = "public.test_table" # some table
|
|
52
|
+
|
|
53
|
+
with open(file_name, "rb") as fileobj:
|
|
54
|
+
dumper.write_dump(
|
|
55
|
+
fileobj,
|
|
56
|
+
table_name,
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Write from PostgreSQL into PostgreSQL
|
|
61
|
+
|
|
62
|
+
Same server
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
|
|
66
|
+
table_dest = "public.test_table_write" # some table for write
|
|
67
|
+
table_src = "public.test_table_read" # some table for read
|
|
68
|
+
query_src = "select ..." # or some sql query for read
|
|
69
|
+
|
|
70
|
+
dumper.write_between(
|
|
71
|
+
table_dest,
|
|
72
|
+
table_src,
|
|
73
|
+
query_src,
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Different servers
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
|
|
81
|
+
connector_src = PGConnector(
|
|
82
|
+
host = <host src>,
|
|
83
|
+
dbname = <database src>,
|
|
84
|
+
user = <username src>,
|
|
85
|
+
password = <password src>,
|
|
86
|
+
port = <port src>,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
dumper_src = PGPackDumper(connector=connector_src)
|
|
90
|
+
|
|
91
|
+
table_dest = "public.test_table_write" # some table for write
|
|
92
|
+
table_src = "public.test_table_read" # some table for read
|
|
93
|
+
query_src = "select ..." # or some sql query for read
|
|
94
|
+
|
|
95
|
+
dumper.write_between(
|
|
96
|
+
table_dest,
|
|
97
|
+
table_src,
|
|
98
|
+
query_src,
|
|
99
|
+
dumper_src.cursor,
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Open PGPack file format
|
|
104
|
+
|
|
105
|
+
Get info from my another repository https://github.com/0xMihalich/pgpack
|
|
106
|
+
|
|
107
|
+
## Installation
|
|
108
|
+
|
|
109
|
+
### From pip
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pip install pgpack_dumper
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### From local directory
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pip install .
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### From git
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install git+https://github.com/0xMihalich/pgpack_dumper
|
|
125
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Library for read and write PGPack format between PostgreSQL and file."""
|
|
2
|
+
|
|
3
|
+
from pgcopylib import PGCopy
|
|
4
|
+
from pgpack import (
|
|
5
|
+
CompressionMethod,
|
|
6
|
+
PGPackReader,
|
|
7
|
+
PGPackWriter,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from .connector import PGConnector
|
|
11
|
+
from .copy import CopyBuffer
|
|
12
|
+
from .dumper import PGPackDumper
|
|
13
|
+
from .errors import (
|
|
14
|
+
CopyBufferError,
|
|
15
|
+
CopyBufferObjectError,
|
|
16
|
+
CopyBufferTableNotDefined,
|
|
17
|
+
PGPackDumperError,
|
|
18
|
+
)
|
|
19
|
+
from .version import __version__
|
|
20
|
+
|
|
21
|
+
__all__ = (
|
|
22
|
+
"__version__",
|
|
23
|
+
"CompressionMethod",
|
|
24
|
+
"CopyBuffer",
|
|
25
|
+
"CopyBufferError",
|
|
26
|
+
"CopyBufferObjectError",
|
|
27
|
+
"CopyBufferTableNotDefined",
|
|
28
|
+
"PGConnector",
|
|
29
|
+
"PGCopy",
|
|
30
|
+
"PGPackDumper",
|
|
31
|
+
"PGPackDumperError",
|
|
32
|
+
"PGPackReader",
|
|
33
|
+
"PGPackWriter",
|
|
34
|
+
)
|
|
35
|
+
__author__ = "0xMihalich"
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from io import BufferedReader
|
|
2
|
+
from logging import Logger
|
|
3
|
+
from typing import (
|
|
4
|
+
Generator,
|
|
5
|
+
Iterator,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from psycopg import (
|
|
9
|
+
Copy,
|
|
10
|
+
Cursor,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .errors import (
|
|
14
|
+
CopyBufferObjectError,
|
|
15
|
+
CopyBufferTableNotDefined,
|
|
16
|
+
)
|
|
17
|
+
from .query_template import query_template
|
|
18
|
+
from .search_object import search_object
|
|
19
|
+
from .structs import PGObject
|
|
20
|
+
from .metadata import read_metadata
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CopyBuffer:
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
cursor: Cursor,
|
|
28
|
+
logger: Logger,
|
|
29
|
+
query: str | None = None,
|
|
30
|
+
table_name: str | None = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""Class initialization."""
|
|
33
|
+
|
|
34
|
+
self.cursor = cursor
|
|
35
|
+
self.logger = logger
|
|
36
|
+
self.query = query
|
|
37
|
+
self.table_name = table_name
|
|
38
|
+
self.pos = 0
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def metadata(self) -> bytes:
|
|
42
|
+
"""Get metadata as bytes."""
|
|
43
|
+
|
|
44
|
+
host = self.cursor.connection.info.host
|
|
45
|
+
self.logger.info(f"Start read metadata from host {host}.")
|
|
46
|
+
metadata = read_metadata(
|
|
47
|
+
self.cursor,
|
|
48
|
+
self.query,
|
|
49
|
+
self.table_name,
|
|
50
|
+
)
|
|
51
|
+
self.logger.info(f"Read metadata from host {host} done.")
|
|
52
|
+
return metadata
|
|
53
|
+
|
|
54
|
+
def copy_to(self) -> Iterator[Copy]:
|
|
55
|
+
"""Get copy object from PostgreSQL."""
|
|
56
|
+
|
|
57
|
+
if not self.query and not self.table_name:
|
|
58
|
+
error_msg = "Query or table not defined."
|
|
59
|
+
self.logger.error(error_msg)
|
|
60
|
+
raise CopyBufferTableNotDefined(error_msg)
|
|
61
|
+
|
|
62
|
+
host = self.cursor.connection.info.host
|
|
63
|
+
|
|
64
|
+
if not self.query:
|
|
65
|
+
self.logger.info(f"Start read from {host}.{self.table_name}.")
|
|
66
|
+
self.cursor.execute(query_template("relkind").format(
|
|
67
|
+
table_name=self.table_name,
|
|
68
|
+
))
|
|
69
|
+
relkind = self.cursor.fetchone()[0]
|
|
70
|
+
pg_object = PGObject[relkind]
|
|
71
|
+
if not pg_object.is_readable:
|
|
72
|
+
error_msg = f"Read from {pg_object} not support."
|
|
73
|
+
self.logger.error(error_msg)
|
|
74
|
+
raise CopyBufferObjectError(error_msg)
|
|
75
|
+
self.logger.info(f"Use method read from {pg_object}.")
|
|
76
|
+
if not pg_object.is_readobject:
|
|
77
|
+
self.table_name = f"(select * from {self.table_name})"
|
|
78
|
+
elif self.query:
|
|
79
|
+
self.logger.info(f"Start read query from {host}.")
|
|
80
|
+
self.logger.info("Use method read from select.")
|
|
81
|
+
self.table_name = f"({self.query})"
|
|
82
|
+
|
|
83
|
+
return self.cursor.copy(
|
|
84
|
+
query_template("copy_to").format(table_name=self.table_name)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def copy_from(
|
|
88
|
+
self,
|
|
89
|
+
copyobj: BufferedReader,
|
|
90
|
+
) -> None:
|
|
91
|
+
"""Write PGCopy dump into PostgreSQL."""
|
|
92
|
+
|
|
93
|
+
if not self.table_name:
|
|
94
|
+
error_msg = "Table not defined."
|
|
95
|
+
self.logger.error(error_msg)
|
|
96
|
+
raise CopyBufferTableNotDefined(error_msg)
|
|
97
|
+
|
|
98
|
+
host = self.cursor.connection.info.host
|
|
99
|
+
self.logger.info(f"Start write into {host}.{self.table_name}.")
|
|
100
|
+
|
|
101
|
+
with self.cursor.copy(
|
|
102
|
+
query_template("copy_from").format(table_name=self.table_name)
|
|
103
|
+
) as cp:
|
|
104
|
+
while chunk := copyobj.read(262_144):
|
|
105
|
+
cp.write(chunk)
|
|
106
|
+
|
|
107
|
+
self.logger.info(f"Write into {host}.{self.table_name} done.")
|
|
108
|
+
|
|
109
|
+
def copy_between(
|
|
110
|
+
self,
|
|
111
|
+
copy_buffer: "CopyBuffer",
|
|
112
|
+
) -> None:
|
|
113
|
+
"""Write from PostgreSQL into PostgreSQL."""
|
|
114
|
+
|
|
115
|
+
with copy_buffer.copy_to() as copy_to:
|
|
116
|
+
destination_host = self.cursor.connection.info.host
|
|
117
|
+
source_host = copy_buffer.cursor.connection.info.host
|
|
118
|
+
source_object = search_object(
|
|
119
|
+
copy_buffer.table_name,
|
|
120
|
+
copy_buffer.query,
|
|
121
|
+
)
|
|
122
|
+
self.logger.info(
|
|
123
|
+
f"Copy {source_object} from {source_host} into "
|
|
124
|
+
f"{destination_host}.{self.table_name} started."
|
|
125
|
+
)
|
|
126
|
+
with self.cursor.copy(
|
|
127
|
+
query_template("copy_from").format(table_name=self.table_name)
|
|
128
|
+
) as copy_from:
|
|
129
|
+
[copy_from.write(data) for data in copy_to]
|
|
130
|
+
self.logger.info(
|
|
131
|
+
f"Copy {source_object} from {source_host}"
|
|
132
|
+
f"into {destination_host}.{self.table_name} done."
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def copy_reader(self, size: int = -1) -> Generator[bytes, None, None]:
|
|
136
|
+
"""Read bytes from copy object."""
|
|
137
|
+
|
|
138
|
+
host = self.cursor.connection.info.host
|
|
139
|
+
source = search_object(
|
|
140
|
+
self.table_name,
|
|
141
|
+
self.query,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
with self.copy_to() as copy_object:
|
|
145
|
+
for data in copy_object:
|
|
146
|
+
self.pos += len(data)
|
|
147
|
+
if size != -1 and self.pos >= size:
|
|
148
|
+
try:
|
|
149
|
+
end_pos = size % (self.pos - len(data))
|
|
150
|
+
except ZeroDivisionError:
|
|
151
|
+
end_pos = size
|
|
152
|
+
yield data[:end_pos]
|
|
153
|
+
break
|
|
154
|
+
yield data
|
|
155
|
+
|
|
156
|
+
self.logger.info(f"Read {source} from {host} done.")
|
|
157
|
+
|
|
158
|
+
def read(self, size: int = -1) -> bytes:
|
|
159
|
+
"""Read bytes from copy object."""
|
|
160
|
+
|
|
161
|
+
return b"".join(self.copy_reader(size))
|
|
162
|
+
|
|
163
|
+
def tell(self) -> int:
|
|
164
|
+
"""Get read size."""
|
|
165
|
+
|
|
166
|
+
return self.pos
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from io import (
|
|
2
|
+
BufferedReader,
|
|
3
|
+
BufferedWriter,
|
|
4
|
+
)
|
|
5
|
+
from logging import Logger
|
|
6
|
+
from types import MethodType
|
|
7
|
+
|
|
8
|
+
from pgpack import (
|
|
9
|
+
CompressionMethod,
|
|
10
|
+
PGPackReader,
|
|
11
|
+
PGPackWriter,
|
|
12
|
+
)
|
|
13
|
+
from psycopg import (
|
|
14
|
+
Connection,
|
|
15
|
+
Cursor,
|
|
16
|
+
)
|
|
17
|
+
from sqlparse import format as sql_format
|
|
18
|
+
|
|
19
|
+
from .copy import CopyBuffer
|
|
20
|
+
from .connector import PGConnector
|
|
21
|
+
from .errors import PGPackDumperError
|
|
22
|
+
from .logger import DumperLogger
|
|
23
|
+
from .multiquery import chunk_query
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PGPackDumper:
|
|
27
|
+
"""Class for read and write PGPack format."""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
connector: PGConnector,
|
|
32
|
+
compression_method: CompressionMethod = CompressionMethod.LZ4,
|
|
33
|
+
logger: Logger = DumperLogger(),
|
|
34
|
+
) -> None:
|
|
35
|
+
"""Class initialization."""
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
self.connector: PGConnector = connector
|
|
39
|
+
self.connect: Connection = Connection.connect(
|
|
40
|
+
**self.connector._asdict()
|
|
41
|
+
)
|
|
42
|
+
self.cursor: Cursor = self.connect.cursor()
|
|
43
|
+
self.compression_method: CompressionMethod = compression_method
|
|
44
|
+
self.logger = logger
|
|
45
|
+
self.copy_buffer: CopyBuffer = CopyBuffer(self.cursor, self.logger)
|
|
46
|
+
except Exception as error:
|
|
47
|
+
logger.error(error)
|
|
48
|
+
raise PGPackDumperError(error)
|
|
49
|
+
|
|
50
|
+
self.logger.info(
|
|
51
|
+
f"PGPackDumper initialized for host {self.connector.host}."
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def multiquery(dump_method: MethodType):
|
|
56
|
+
"""Multiquery decorator."""
|
|
57
|
+
|
|
58
|
+
def wrapper(*args, **kwargs):
|
|
59
|
+
|
|
60
|
+
first_part: list[str]
|
|
61
|
+
second_part: list[str]
|
|
62
|
+
|
|
63
|
+
self: PGPackDumper = args[0]
|
|
64
|
+
cursor: Cursor = kwargs.get("cursor_src") or self.cursor
|
|
65
|
+
query: str = kwargs.get("query_src") or kwargs.get("query")
|
|
66
|
+
part: int = 0
|
|
67
|
+
first_part, second_part = chunk_query(self.query_formatter(query))
|
|
68
|
+
|
|
69
|
+
if first_part:
|
|
70
|
+
self.logger.info("Multiquery detected.")
|
|
71
|
+
|
|
72
|
+
for query in first_part:
|
|
73
|
+
self.logger.info(f"Execute query {part}.")
|
|
74
|
+
cursor.execute(query)
|
|
75
|
+
part += 1
|
|
76
|
+
|
|
77
|
+
if second_part:
|
|
78
|
+
for key in ("query", "query_src"):
|
|
79
|
+
if key in kwargs:
|
|
80
|
+
kwargs[key] = second_part.pop(0)
|
|
81
|
+
break
|
|
82
|
+
|
|
83
|
+
self.logger.info(f"Execute query {part or ''}(copy method).")
|
|
84
|
+
dump_method(*args, **kwargs)
|
|
85
|
+
|
|
86
|
+
if second_part:
|
|
87
|
+
for query in second_part:
|
|
88
|
+
part += 1
|
|
89
|
+
self.logger.info(f"Execute query {part}.")
|
|
90
|
+
cursor.execute(query)
|
|
91
|
+
|
|
92
|
+
return wrapper
|
|
93
|
+
|
|
94
|
+
def query_formatter(self, query: str) -> str | None:
|
|
95
|
+
"""Reformat query."""
|
|
96
|
+
|
|
97
|
+
if not query:
|
|
98
|
+
return
|
|
99
|
+
return sql_format(sql=query, strip_comments=True).strip().strip(";")
|
|
100
|
+
|
|
101
|
+
def make_buffer_obj(
|
|
102
|
+
self,
|
|
103
|
+
cursor: Cursor | None = None,
|
|
104
|
+
query: str | None = None,
|
|
105
|
+
table_name: str | None = None,
|
|
106
|
+
) -> CopyBuffer:
|
|
107
|
+
"""Make new buffer object for read."""
|
|
108
|
+
|
|
109
|
+
cursor = cursor or Connection.connect(
|
|
110
|
+
**self.connector._asdict()
|
|
111
|
+
).cursor()
|
|
112
|
+
host = cursor.connection.info.host
|
|
113
|
+
self.logger.info(f"Make new cursor object for host {host}.")
|
|
114
|
+
|
|
115
|
+
return CopyBuffer(
|
|
116
|
+
cursor,
|
|
117
|
+
query,
|
|
118
|
+
table_name,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
@multiquery
|
|
122
|
+
def read_dump(
|
|
123
|
+
self,
|
|
124
|
+
fileobj: BufferedWriter,
|
|
125
|
+
query: str | None = None,
|
|
126
|
+
table_name: str | None = None,
|
|
127
|
+
) -> None:
|
|
128
|
+
"""Read PGPack dump from PostgreSQL/GreenPlum."""
|
|
129
|
+
|
|
130
|
+
pgpack = PGPackWriter(fileobj, self.compression_method)
|
|
131
|
+
self.copy_buffer.query = query
|
|
132
|
+
self.copy_buffer.table_name = table_name
|
|
133
|
+
pgpack.write(
|
|
134
|
+
self.copy_buffer.metadata,
|
|
135
|
+
self.copy_buffer,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def write_dump(
|
|
139
|
+
self,
|
|
140
|
+
fileobj: BufferedReader,
|
|
141
|
+
table_name: str,
|
|
142
|
+
) -> None:
|
|
143
|
+
"""Write PGPack dump into PostgreSQL/GreenPlum."""
|
|
144
|
+
|
|
145
|
+
fileobj.seek(0)
|
|
146
|
+
pgpack = PGPackReader(fileobj)
|
|
147
|
+
pgpack.pgcopy_compressor.seek(0)
|
|
148
|
+
self.copy_buffer.table_name = table_name
|
|
149
|
+
self.copy_buffer.copy_from(pgpack.pgcopy_compressor)
|
|
150
|
+
self.connect.commit()
|
|
151
|
+
|
|
152
|
+
@multiquery
|
|
153
|
+
def write_between(
|
|
154
|
+
self,
|
|
155
|
+
table_dest: str,
|
|
156
|
+
table_src: str | None = None,
|
|
157
|
+
query_src: str | None = None,
|
|
158
|
+
cursor_src: Cursor | None = None,
|
|
159
|
+
) -> None:
|
|
160
|
+
"""Write from PostgreSQL/GreenPlum into PostgreSQL/GreenPlum."""
|
|
161
|
+
|
|
162
|
+
source_copy_buffer = self.make_buffer_obj(
|
|
163
|
+
cursor=cursor_src,
|
|
164
|
+
query=query_src,
|
|
165
|
+
table_name=table_src,
|
|
166
|
+
)
|
|
167
|
+
self.copy_buffer.table_name = table_dest
|
|
168
|
+
self.copy_buffer.copy_between(source_copy_buffer)
|
|
169
|
+
self.connect.commit()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class CopyBufferError(Exception):
|
|
2
|
+
"""CopyBuffer base error."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CopyBufferObjectError(TypeError):
|
|
6
|
+
"""Destination object not support."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CopyBufferTableNotDefined(ValueError):
|
|
10
|
+
"""Destination table not defined."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PGPackDumperError(Exception):
|
|
14
|
+
"""PGPackDumper base error."""
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from logging import (
|
|
3
|
+
DEBUG,
|
|
4
|
+
FileHandler,
|
|
5
|
+
Formatter,
|
|
6
|
+
Logger,
|
|
7
|
+
StreamHandler,
|
|
8
|
+
)
|
|
9
|
+
from os import makedirs
|
|
10
|
+
from os.path import dirname
|
|
11
|
+
from sys import stdout
|
|
12
|
+
|
|
13
|
+
from .version import __version__
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def root_dir() -> str:
|
|
17
|
+
"""Get project directory."""
|
|
18
|
+
|
|
19
|
+
import __main__
|
|
20
|
+
|
|
21
|
+
return dirname(__main__.__file__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DumperLogger(Logger):
|
|
25
|
+
"""PGPackDumper logger."""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
level: int = DEBUG,
|
|
30
|
+
use_console: bool = True,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""Class initialize."""
|
|
33
|
+
|
|
34
|
+
super().__init__("PGPackDumper")
|
|
35
|
+
|
|
36
|
+
self.fmt = (
|
|
37
|
+
f"%(asctime)s | %(levelname)-8s | ver {__version__}"
|
|
38
|
+
"| %(funcName)s-%(filename)s-%(lineno)04d <%(message)s>"
|
|
39
|
+
)
|
|
40
|
+
self.setLevel(level)
|
|
41
|
+
self.log_path = f"{root_dir()}/pgpack_logs"
|
|
42
|
+
makedirs(self.log_path, exist_ok=True)
|
|
43
|
+
|
|
44
|
+
formatter = Formatter(
|
|
45
|
+
fmt=self.fmt,
|
|
46
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
file_handler = FileHandler(
|
|
50
|
+
"{}/{:%Y-%m-%d}_{}.log".format(
|
|
51
|
+
self.log_path,
|
|
52
|
+
datetime.now(),
|
|
53
|
+
self.name,
|
|
54
|
+
),
|
|
55
|
+
encoding="utf-8",
|
|
56
|
+
)
|
|
57
|
+
file_handler.setLevel(DEBUG)
|
|
58
|
+
file_handler.setFormatter(formatter)
|
|
59
|
+
self.addHandler(file_handler)
|
|
60
|
+
|
|
61
|
+
if use_console:
|
|
62
|
+
console_handler = StreamHandler(stdout)
|
|
63
|
+
console_handler.setLevel(level)
|
|
64
|
+
console_handler.setFormatter(formatter)
|
|
65
|
+
self.addHandler(console_handler)
|
|
66
|
+
|
|
67
|
+
self.propagate = False
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from psycopg import Cursor
|
|
2
|
+
|
|
3
|
+
from .query_template import query_template
|
|
4
|
+
from .random_name import random_name
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def read_metadata(
|
|
8
|
+
cursor: Cursor,
|
|
9
|
+
query: str | None = None,
|
|
10
|
+
table_name: str | None = None,
|
|
11
|
+
) -> bytes:
|
|
12
|
+
"""Read metadata for query or table."""
|
|
13
|
+
|
|
14
|
+
if not query and not table_name:
|
|
15
|
+
raise ValueError()
|
|
16
|
+
|
|
17
|
+
if query:
|
|
18
|
+
session_name = random_name()
|
|
19
|
+
prepare_name = f"{session_name}_prepare"
|
|
20
|
+
table_name = f"{session_name}_temp"
|
|
21
|
+
cursor.execute(query_template("prepare").format(
|
|
22
|
+
prepare_name=prepare_name,
|
|
23
|
+
query=query,
|
|
24
|
+
table_name=table_name,
|
|
25
|
+
))
|
|
26
|
+
|
|
27
|
+
cursor.execute(query_template("attributes").format(
|
|
28
|
+
table_name=table_name,
|
|
29
|
+
))
|
|
30
|
+
|
|
31
|
+
metadata: bytes = cursor.fetchone()[0]
|
|
32
|
+
|
|
33
|
+
if query:
|
|
34
|
+
cursor.execute(f"drop table if exists {table_name};")
|
|
35
|
+
|
|
36
|
+
return metadata
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
def chunk_query(query: str | None) -> tuple[list[str]]:
|
|
2
|
+
"""Chunk multiquery to queryes."""
|
|
3
|
+
|
|
4
|
+
if not query:
|
|
5
|
+
return [], []
|
|
6
|
+
|
|
7
|
+
first_part: list[str] = [
|
|
8
|
+
part.strip()
|
|
9
|
+
for part in query.split(";")
|
|
10
|
+
]
|
|
11
|
+
second_part: list[str] = []
|
|
12
|
+
|
|
13
|
+
for _ in first_part:
|
|
14
|
+
second_part.append(first_part.pop())
|
|
15
|
+
if any(
|
|
16
|
+
word == second_part[-1][:len(word)].lower()
|
|
17
|
+
for word in ("with", "select")
|
|
18
|
+
):
|
|
19
|
+
second_part = list(reversed(second_part))
|
|
20
|
+
break
|
|
21
|
+
|
|
22
|
+
return first_part, second_part
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
select json_agg(json_build_array(attnum, json_build_array(attname, atttypid::int4,
|
|
2
|
+
case when atttypid = 1042 then atttypmod - 4 when atttypid = 1700 then (atttypmod - 4) >> 16 else attlen end,
|
|
3
|
+
case when atttypid = 1700 then (atttypmod - 4) & 65535 else 0 end)))::text::bytea as metadata
|
|
4
|
+
from pg_attribute where attrelid = '{table_name}'::regclass and attnum > 0 and not attisdropped;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
copy {table_name} from stdin with (format binary);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
copy {table_name} to stdout with (format binary);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
select relkind from pg_class where oid = '{table_name}'::regclass;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from re import match
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
pattern = r"\(select \* from (.*)\)|(.*)"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def search_object(table: str, query: str = "") -> str:
|
|
8
|
+
"""Return current string for object."""
|
|
9
|
+
|
|
10
|
+
if query:
|
|
11
|
+
return "query"
|
|
12
|
+
|
|
13
|
+
return match(pattern, table).group(1) or table
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import NamedTuple
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RelClass(NamedTuple):
|
|
6
|
+
"""Postgres objects."""
|
|
7
|
+
|
|
8
|
+
rel_name: str
|
|
9
|
+
is_readobject: bool
|
|
10
|
+
is_readable: bool
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PGObject(RelClass, Enum):
|
|
14
|
+
"""RelClass object from relkind value."""
|
|
15
|
+
|
|
16
|
+
r = RelClass("Relation table", True, True)
|
|
17
|
+
i = RelClass("Index", False, False)
|
|
18
|
+
S = RelClass("Sequence", False, False)
|
|
19
|
+
t = RelClass("Toast table", False, False)
|
|
20
|
+
v = RelClass("View", False, True)
|
|
21
|
+
m = RelClass("Materialized view", False, True)
|
|
22
|
+
c = RelClass("Composite type", False, False)
|
|
23
|
+
f = RelClass("Foreign table", False, True)
|
|
24
|
+
p = RelClass("Partitioned table", True, True)
|
|
25
|
+
I = RelClass("Partitioned index", False, True) # noqa: E741
|
|
26
|
+
u = RelClass("Temporary table", True, True)
|
|
27
|
+
o = RelClass("Optimized files", False, False)
|
|
28
|
+
b = RelClass("Block directory", False, False)
|
|
29
|
+
M = RelClass("Visibility map", False, False)
|
|
30
|
+
|
|
31
|
+
def __str__(self) -> str:
|
|
32
|
+
"""String representation class."""
|
|
33
|
+
|
|
34
|
+
return self.rel_name
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.2"
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pgpack-dumper
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Library for read and write PGPack format between PostgreSQL and file.
|
|
5
|
+
Home-page: https://github.com/0xMihalich/pgpack_dumper
|
|
6
|
+
Author: 0xMihalich
|
|
7
|
+
Author-email: bayanmobile87@gmail.com
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: README.md
|
|
10
|
+
License-File: CHANGELOG.md
|
|
11
|
+
Requires-Dist: pgpack==0.1.3
|
|
12
|
+
Requires-Dist: psycopg>=3.2.9
|
|
13
|
+
Requires-Dist: sqlparse>=0.5.3
|
|
14
|
+
|
|
15
|
+
# PGPackDumper
|
|
16
|
+
|
|
17
|
+
Library for read and write PGPack format between PostgreSQL and file
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### Initialization
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from pgpack_dumper import (
|
|
25
|
+
CompressionMethod,
|
|
26
|
+
PGConnector,
|
|
27
|
+
PGPackDumper,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
connector = PGConnector(
|
|
31
|
+
host = <your host>,
|
|
32
|
+
dbname = <your database>,
|
|
33
|
+
user = <your username>,
|
|
34
|
+
password = <your password>,
|
|
35
|
+
port = <your port>,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
dumper = PGPackDumper(
|
|
39
|
+
connector=connector,
|
|
40
|
+
compression_method=CompressionMethod.LZ4, # or CompressionMethod.ZSTD or CompressionMethod.NONE
|
|
41
|
+
)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Read dump from PostgreSQL into file
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
file_name = "pgpack.lz4"
|
|
48
|
+
# you need define one of parameter query or table_name
|
|
49
|
+
query = "select ..." # some sql query
|
|
50
|
+
table_name = "public.test_table" # or some table
|
|
51
|
+
|
|
52
|
+
with open(file_name, "wb") as fileobj:
|
|
53
|
+
dumper.read_dump(
|
|
54
|
+
fileobj,
|
|
55
|
+
query,
|
|
56
|
+
table_name,
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Write dump from file into PostgreSQL
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
file_name = "pgpack.lz4"
|
|
64
|
+
# you need define one of parameter table_name
|
|
65
|
+
table_name = "public.test_table" # some table
|
|
66
|
+
|
|
67
|
+
with open(file_name, "rb") as fileobj:
|
|
68
|
+
dumper.write_dump(
|
|
69
|
+
fileobj,
|
|
70
|
+
table_name,
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Write from PostgreSQL into PostgreSQL
|
|
75
|
+
|
|
76
|
+
Same server
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
|
|
80
|
+
table_dest = "public.test_table_write" # some table for write
|
|
81
|
+
table_src = "public.test_table_read" # some table for read
|
|
82
|
+
query_src = "select ..." # or some sql query for read
|
|
83
|
+
|
|
84
|
+
dumper.write_between(
|
|
85
|
+
table_dest,
|
|
86
|
+
table_src,
|
|
87
|
+
query_src,
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Different servers
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
|
|
95
|
+
connector_src = PGConnector(
|
|
96
|
+
host = <host src>,
|
|
97
|
+
dbname = <database src>,
|
|
98
|
+
user = <username src>,
|
|
99
|
+
password = <password src>,
|
|
100
|
+
port = <port src>,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
dumper_src = PGPackDumper(connector=connector_src)
|
|
104
|
+
|
|
105
|
+
table_dest = "public.test_table_write" # some table for write
|
|
106
|
+
table_src = "public.test_table_read" # some table for read
|
|
107
|
+
query_src = "select ..." # or some sql query for read
|
|
108
|
+
|
|
109
|
+
dumper.write_between(
|
|
110
|
+
table_dest,
|
|
111
|
+
table_src,
|
|
112
|
+
query_src,
|
|
113
|
+
dumper_src.cursor,
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Open PGPack file format
|
|
118
|
+
|
|
119
|
+
Get info from my another repository https://github.com/0xMihalich/pgpack
|
|
120
|
+
|
|
121
|
+
## Installation
|
|
122
|
+
|
|
123
|
+
### From pip
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pip install pgpack_dumper
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### From local directory
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pip install .
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### From git
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pip install git+https://github.com/0xMihalich/pgpack_dumper
|
|
139
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
CHANGELOG.md
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
requirements.txt
|
|
6
|
+
setup.cfg
|
|
7
|
+
setup.py
|
|
8
|
+
pgpack_dumper/__init__.py
|
|
9
|
+
pgpack_dumper/connector.py
|
|
10
|
+
pgpack_dumper/copy.py
|
|
11
|
+
pgpack_dumper/dumper.py
|
|
12
|
+
pgpack_dumper/errors.py
|
|
13
|
+
pgpack_dumper/logger.py
|
|
14
|
+
pgpack_dumper/metadata.py
|
|
15
|
+
pgpack_dumper/multiquery.py
|
|
16
|
+
pgpack_dumper/query_path.py
|
|
17
|
+
pgpack_dumper/query_template.py
|
|
18
|
+
pgpack_dumper/random_name.py
|
|
19
|
+
pgpack_dumper/search_object.py
|
|
20
|
+
pgpack_dumper/structs.py
|
|
21
|
+
pgpack_dumper/version.py
|
|
22
|
+
pgpack_dumper.egg-info/PKG-INFO
|
|
23
|
+
pgpack_dumper.egg-info/SOURCES.txt
|
|
24
|
+
pgpack_dumper.egg-info/dependency_links.txt
|
|
25
|
+
pgpack_dumper.egg-info/not-zip-safe
|
|
26
|
+
pgpack_dumper.egg-info/requires.txt
|
|
27
|
+
pgpack_dumper.egg-info/top_level.txt
|
|
28
|
+
pgpack_dumper/queryes/attributes.sql
|
|
29
|
+
pgpack_dumper/queryes/copy_from.sql
|
|
30
|
+
pgpack_dumper/queryes/copy_to.sql
|
|
31
|
+
pgpack_dumper/queryes/prepare.sql
|
|
32
|
+
pgpack_dumper/queryes/relkind.sql
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pgpack_dumper
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
from setuptools import (
|
|
3
|
+
find_packages,
|
|
4
|
+
setup,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
shutil.rmtree("build", ignore_errors=True)
|
|
8
|
+
shutil.rmtree("pgpack.egg-info", ignore_errors=True)
|
|
9
|
+
|
|
10
|
+
with open(file="README.md", encoding="utf-8") as f:
|
|
11
|
+
long_description = f.read()
|
|
12
|
+
|
|
13
|
+
setup(
|
|
14
|
+
name="pgpack_dumper",
|
|
15
|
+
version="0.1.2",
|
|
16
|
+
packages=find_packages(),
|
|
17
|
+
author="0xMihalich",
|
|
18
|
+
author_email="bayanmobile87@gmail.com",
|
|
19
|
+
description=(
|
|
20
|
+
"Library for read and write PGPack "
|
|
21
|
+
"format between PostgreSQL and file."
|
|
22
|
+
),
|
|
23
|
+
url="https://github.com/0xMihalich/pgpack_dumper",
|
|
24
|
+
long_description=long_description,
|
|
25
|
+
long_description_content_type="text/markdown",
|
|
26
|
+
zip_safe=False,
|
|
27
|
+
package_data={
|
|
28
|
+
"pgpack_dumper.queryes": ["*.sql"],
|
|
29
|
+
},
|
|
30
|
+
include_package_data=True,
|
|
31
|
+
)
|