Skip to content

Commit b58580a

Browse files
authored
Merge pull request #17 from pangpang20/master
Enhancements to GaussDB CI and Logical Replication Testing
2 parents 5de76c7 + 792d67f commit b58580a

File tree

5 files changed

+272
-18
lines changed

5 files changed

+272
-18
lines changed

.github/workflows/release.yml renamed to .github/workflows/release-gaussdb-pool.yml

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
name: Build and Release
1+
---
2+
name: Build and Release gaussdb_pool
23

34
on:
45
push:
56
tags:
6-
- "*"
7+
- "pool-v*.*.*"
78

89
permissions:
910
contents: write
@@ -26,30 +27,18 @@ jobs:
2627
pip install --upgrade pip
2728
pip install --upgrade setuptools wheel build
2829
29-
- name: Build gaussdb
30-
working-directory: gaussdb
31-
run: python -m build
32-
3330
- name: Build gaussdb_pool
3431
working-directory: gaussdb_pool
3532
run: python -m build
3633

37-
- name: Build isort_gaussdb
38-
working-directory: tools/isort-gaussdb
39-
run: python -m build
40-
4134
- name: Show dist dirs content
4235
run: |
43-
ls -l gaussdb/dist/
4436
ls -l gaussdb_pool/dist/
45-
ls -l tools/isort-gaussdb/dist/
4637
4738
- name: Collect all artifacts
4839
run: |
4940
mkdir -p all_dist
50-
cp gaussdb/dist/* all_dist/
5141
cp gaussdb_pool/dist/* all_dist/
52-
cp tools/isort-gaussdb/dist/* all_dist/
5342
ls -ltr all_dist/
5443
5544
- name: Upload all dist/* to GitHub Release
@@ -64,4 +53,4 @@ jobs:
6453
twine upload all_dist/*
6554
env:
6655
TWINE_USERNAME: __token__
67-
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
56+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}

.github/workflows/release-gaussdb.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
name: Build and Release gaussdb
3+
4+
on:
5+
push:
6+
tags:
7+
- "v*.*.*"
8+
9+
permissions:
10+
contents: write
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-22.04
15+
16+
steps:
17+
- name: Checkout source
18+
uses: actions/checkout@v3
19+
20+
- name: Set up Python
21+
uses: actions/setup-python@v4
22+
with:
23+
python-version: "3.9"
24+
25+
- name: Install build tools
26+
run: |
27+
pip install --upgrade pip
28+
pip install --upgrade setuptools wheel build
29+
30+
- name: Build gaussdb
31+
working-directory: gaussdb
32+
run: python -m build
33+
34+
- name: Show dist dirs content
35+
run: |
36+
ls -l gaussdb/dist/
37+
38+
- name: Collect all artifacts
39+
run: |
40+
mkdir -p all_dist
41+
cp gaussdb/dist/* all_dist/
42+
ls -ltr all_dist/
43+
44+
- name: Upload all dist/* to GitHub Release
45+
uses: softprops/action-gh-release@v1
46+
with:
47+
files: all_dist/*
48+
49+
- name: Upload to PyPI
50+
if: startsWith(github.ref, 'refs/tags/')
51+
run: |
52+
pip install --upgrade twine
53+
twine upload all_dist/*
54+
env:
55+
TWINE_USERNAME: __token__
56+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
name: Build and Release isort-gaussdb
3+
4+
on:
5+
push:
6+
tags:
7+
- "isort-v*.*.*"
8+
9+
permissions:
10+
contents: write
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-22.04
15+
16+
steps:
17+
- name: Checkout source
18+
uses: actions/checkout@v3
19+
20+
- name: Set up Python
21+
uses: actions/setup-python@v4
22+
with:
23+
python-version: "3.9"
24+
25+
- name: Install build tools
26+
run: |
27+
pip install --upgrade pip
28+
pip install --upgrade setuptools wheel build
29+
30+
- name: Build isort-gaussdb
31+
working-directory: isort-gaussdb
32+
run: python -m build
33+
34+
- name: Show dist dirs content
35+
run: |
36+
ls -l isort-gaussdb/dist/
37+
38+
- name: Collect all artifacts
39+
run: |
40+
mkdir -p all_dist
41+
cp isort-gaussdb/dist/* all_dist/
42+
ls -ltr all_dist/
43+
44+
- name: Upload all dist/* to GitHub Release
45+
uses: softprops/action-gh-release@v1
46+
with:
47+
files: all_dist/*
48+
49+
- name: Upload to PyPI
50+
if: startsWith(github.ref, 'refs/tags/')
51+
run: |
52+
pip install --upgrade twine
53+
twine upload all_dist/*
54+
env:
55+
TWINE_USERNAME: __token__
56+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}

tests/test_column.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,14 @@ def skip_neg_scale(*args):
108108
],
109109
)
110110
def test_details(conn, type, precision, scale, dsize, isize):
111-
cur = conn.cursor()
112-
cur.execute(f"select null::{type}")
113-
col = cur.description[0]
111+
with conn.cursor() as cur:
112+
cur.execute(f"select null::{type}")
113+
col = cur.description[0]
114+
try:
115+
conn.rollback()
116+
except Exception:
117+
pass
118+
114119
assert type == col.type_display
115120
assert f" {type} " in (repr(col))
116121
assert col.precision == precision

tests/test_logical_decoding.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import pytest
2+
3+
SLOT_NAME = "slot_test"
4+
SCHEMA = "my_schema"
5+
TABLE = "test01"
6+
7+
8+
def _slot_exists(conn, slot_name):
9+
cur = conn.cursor()
10+
cur.execute(
11+
"SELECT count(1) FROM pg_replication_slots WHERE slot_name = %s",
12+
(slot_name,),
13+
)
14+
row = cur.fetchone()
15+
return bool(row and row[0] > 0)
16+
17+
18+
def _cleanup_slot_and_schema(conn):
19+
cur = conn.cursor()
20+
# Drop slot if exists
21+
try:
22+
cur.execute(
23+
"SELECT count(1) FROM pg_replication_slots WHERE slot_name = %s",
24+
(SLOT_NAME,),
25+
)
26+
if cur.fetchone()[0] > 0:
27+
cur.execute("SELECT pg_drop_replication_slot(%s);", (SLOT_NAME,))
28+
except Exception:
29+
pass
30+
31+
# Drop schema cascade
32+
try:
33+
cur.execute(f"DROP SCHEMA IF EXISTS {SCHEMA} CASCADE;")
34+
except Exception:
35+
pass
36+
conn.commit()
37+
38+
39+
@pytest.fixture(scope="function")
40+
def setup_env(conn):
41+
"""Ensure clean environment for each test."""
42+
_cleanup_slot_and_schema(conn)
43+
cur = conn.cursor()
44+
cur.execute(f"CREATE SCHEMA {SCHEMA};")
45+
cur.execute(f"SET search_path TO {SCHEMA};")
46+
cur.execute(f"CREATE TABLE {TABLE} (id int, name varchar(255));")
47+
cur.execute(f"ALTER TABLE {TABLE} REPLICA IDENTITY FULL;")
48+
conn.commit()
49+
yield conn
50+
_cleanup_slot_and_schema(conn)
51+
52+
53+
def _create_slot(cur):
54+
cur.execute(
55+
"SELECT * FROM pg_create_logical_replication_slot(%s, %s);",
56+
(SLOT_NAME, "mppdb_decoding"),
57+
)
58+
59+
60+
def _read_changes(cur):
61+
cur.execute(
62+
"SELECT data FROM pg_logical_slot_get_changes(%s, NULL, %s);",
63+
(SLOT_NAME, 4096),
64+
)
65+
rows = cur.fetchall()
66+
return [str(r[0]) for r in rows if r and r[0] is not None]
67+
68+
69+
def test_create_replication_slot(setup_env):
70+
cur = setup_env.cursor()
71+
_create_slot(cur)
72+
assert _slot_exists(setup_env, SLOT_NAME)
73+
74+
75+
def test_insert_produces_changes(setup_env):
76+
cur = setup_env.cursor()
77+
_create_slot(cur)
78+
assert _slot_exists(setup_env, SLOT_NAME)
79+
80+
# insert
81+
cur.execute(f"INSERT INTO {TABLE} VALUES (%s, %s);", (1, "hello world"))
82+
setup_env.commit()
83+
84+
changes = _read_changes(cur)
85+
joined = "\n".join(changes).lower()
86+
87+
assert "insert" in joined, "Insert event not present"
88+
assert "hello world" in joined, "Inserted value missing"
89+
90+
91+
def test_update_produces_changes(setup_env):
92+
cur = setup_env.cursor()
93+
_create_slot(cur)
94+
assert _slot_exists(setup_env, SLOT_NAME)
95+
96+
# prepare row
97+
cur.execute(f"INSERT INTO {TABLE} VALUES (%s, %s);", (1, "hello world"))
98+
setup_env.commit()
99+
100+
# update
101+
cur.execute(
102+
f"UPDATE {TABLE} SET name = %s WHERE id = %s;",
103+
("hello gaussdb", 1),
104+
)
105+
setup_env.commit()
106+
107+
changes = _read_changes(cur)
108+
joined = "\n".join(changes).lower()
109+
110+
assert "update" in joined, "Update event not present"
111+
assert "hello gaussdb" in joined, "Updated value missing"
112+
113+
114+
def test_delete_produces_changes(setup_env):
115+
cur = setup_env.cursor()
116+
_create_slot(cur)
117+
assert _slot_exists(setup_env, SLOT_NAME)
118+
119+
# prepare row
120+
cur.execute(f"INSERT INTO {TABLE} VALUES (%s, %s);", (1, "to_delete"))
121+
setup_env.commit()
122+
123+
# delete
124+
cur.execute(f"DELETE FROM {TABLE} WHERE id = %s;", (1,))
125+
setup_env.commit()
126+
127+
changes = _read_changes(cur)
128+
joined = "\n".join(changes).lower()
129+
130+
assert "delete" in joined, "Delete event not present"
131+
assert "to_delete" in joined, "Deleted value missing"
132+
133+
134+
def test_drop_replication_slot(setup_env):
135+
cur = setup_env.cursor()
136+
_create_slot(cur)
137+
assert _slot_exists(setup_env, SLOT_NAME)
138+
139+
# drop slot
140+
cur.execute("SELECT pg_drop_replication_slot(%s);", (SLOT_NAME,))
141+
setup_env.commit()
142+
143+
# verify removed
144+
cur.execute(
145+
"SELECT count(1) FROM pg_replication_slots WHERE slot_name = %s;",
146+
(SLOT_NAME,),
147+
)
148+
assert cur.fetchone()[0] == 0

0 commit comments

Comments
 (0)