Skip to content

Commit a1ae127

Browse files
authored
Merge pull request piccolo-orm#17 from piccolo-orm/sql_shell
added sql_shell command
2 parents 3ae137c + 49b91f3 commit a1ae127

File tree

9 files changed

+102
-0
lines changed

9 files changed

+102
-0
lines changed

docs/src/piccolo/projects_and_apps/included_apps.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,20 @@ Launches an iPython shell, and automatically imports all of your registered
7070
7171
piccolo shell run
7272
73+
sql_shell
74+
~~~~~~~~~
75+
76+
Launches a SQL shell (``psql`` or ``sqlite3`` depending on the engine), using
77+
the connection settings defined in ``piccolo_conf.py``. It's convenient if you
78+
need to run raw SQL queries on your database.
79+
80+
.. code-block:: bash
81+
82+
piccolo sql_shell run
83+
84+
For it to work, the underlying command needs to be on the path (i.e. ``psql``
85+
or ``sqlite3`` depending on which you're using).
86+
7387
-------------------------------------------------------------------------------
7488

7589
Optional includes

piccolo/apps/sql_shell/__init__.py

Whitespace-only changes.

piccolo/apps/sql_shell/commands/__init__.py

Whitespace-only changes.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import os
2+
import signal
3+
import subprocess
4+
import typing as t
5+
6+
from piccolo.engine.postgres import PostgresEngine
7+
from piccolo.engine.sqlite import SQLiteEngine
8+
from piccolo.engine.finder import engine_finder
9+
10+
if t.TYPE_CHECKING:
11+
from piccolo.engine.base import Engine
12+
13+
14+
def run():
15+
"""
16+
Launch the SQL shell for the configured engine. For Postgres
17+
this will be psql, and for SQLite it will be sqlite3.
18+
"""
19+
engine: t.Optional[Engine] = engine_finder()
20+
21+
if engine is None:
22+
raise ValueError(
23+
"Unable to find the engine - make sure piccolo_conf is on the "
24+
"path."
25+
)
26+
27+
# Heavily inspired by Django's dbshell command
28+
if isinstance(engine, PostgresEngine):
29+
engine: PostgresEngine = engine
30+
31+
args = ["psql"]
32+
33+
host = engine.config.get("host")
34+
port = engine.config.get("port")
35+
user = engine.config.get("user")
36+
password = engine.config.get("password")
37+
database = engine.config.get("database")
38+
39+
if user:
40+
args += ["-U", user]
41+
if host:
42+
args += ["-h", host]
43+
if port:
44+
args += ["-p", str(port)]
45+
args += [database]
46+
47+
sigint_handler = signal.getsignal(signal.SIGINT)
48+
subprocess_env = os.environ.copy()
49+
if password:
50+
subprocess_env["PGPASSWORD"] = str(password)
51+
try:
52+
# Allow SIGINT to pass to psql to abort queries.
53+
signal.signal(signal.SIGINT, signal.SIG_IGN)
54+
print("Enter \\q to exit")
55+
subprocess.run(args, check=True, env=subprocess_env)
56+
finally:
57+
# Restore the original SIGINT handler.
58+
signal.signal(signal.SIGINT, sigint_handler)
59+
60+
elif isinstance(engine, SQLiteEngine):
61+
engine: SQLiteEngine = engine
62+
print("Enter .quit to exit")
63+
subprocess.run(
64+
["sqlite3", engine.connection_kwargs.get("database")], check=True
65+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from piccolo.conf.apps import AppConfig
2+
from .commands.run import run
3+
4+
5+
APP_CONFIG = AppConfig(
6+
app_name="sql_shell", migrations_folder_path="", commands=[run]
7+
)

piccolo/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from piccolo.apps.playground.piccolo_app import APP_CONFIG as playground_config
1212
from piccolo.apps.project.piccolo_app import APP_CONFIG as project_config
1313
from piccolo.apps.shell.piccolo_app import APP_CONFIG as shell_config
14+
from piccolo.apps.sql_shell.piccolo_app import APP_CONFIG as sql_shell_config
1415
from piccolo.apps.user.piccolo_app import APP_CONFIG as user_config
1516

1617

@@ -53,6 +54,7 @@ def main():
5354
playground_config,
5455
project_config,
5556
shell_config,
57+
sql_shell_config,
5658
user_config,
5759
]:
5860
for command in _app_config.commands:

tests/apps/sql_shell/__init__.py

Whitespace-only changes.

tests/apps/sql_shell/commands/__init__.py

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from unittest import TestCase
2+
from unittest.mock import patch, MagicMock
3+
4+
from piccolo.apps.sql_shell.commands.run import run
5+
6+
7+
class TestRun(TestCase):
8+
@patch("piccolo.apps.sql_shell.commands.run.subprocess")
9+
def test_run(self, subprocess: MagicMock):
10+
"""
11+
A simple test to make sure it executes without raising any exceptions.
12+
"""
13+
run()
14+
self.assertTrue(subprocess.run.called)

0 commit comments

Comments
 (0)