2017-04-04 9 views
1

sqlite3モジュールをPythonで使用すると、SIGINT(Control-Cなど)を受け取ったときに長時間実行されるクエリがすぐに中断/ sqlite3によって提供されるinterrupt()メソッドがありますが、それを使用する方法の例はありません。Python sqlite3:KeyboardInterruptなどで長時間実行中のクエリを素早くきれいに中断する方法

Python/sqlite3経由で実行中の長いクエリを中断/キャンセルする簡単な方法はありますか?

import sqlite3 
from random import randint 

conn = sqlite3.connect("randtable.db", 10.0) 
cursor = conn.cursor() 

cursor.execute("CREATE TABLE randint (id integer, rand integer)") 

for i in range(1000000): 
    if i % 1000 == 0: 
     print ("{0}...".format(i)) 
    rand = randint(0,1000000) 
    cursor.execute("INSERT INTO randint VALUES ({0},{1})".format(i,rand)) 

conn.commit() 
conn.close() 

、端末に長い実行のPython/sqlite3のスクリプトを実行し、コントロール-Cでそれを中断しよう:

from __future__ import print_function 
import sqlite3 

def main(): 
    # Long running query (pathological by design) 
    statement =''' 
SELECT DISTINCT a.id,a.rand 
FROM randint a 
    JOIN randint b ON a.id=b.rand 
    JOIN randint c ON a.id=c.rand 
    JOIN randint d ON a.id=d.rand 
    JOIN randint e ON a.id=e.rand 
    JOIN randint f ON a.id=f.rand 
    JOIN randint g ON a.id=g.rand 
    JOIN randint h ON a.id=h.rand 
ORDER BY a.id limit 10''' 

    conn = sqlite3.connect('randtable.sqlite', 10.0) 
    cursor = conn.cursor() 

    print ("Executing query") 

    cursor.execute(statement) 
    rows = cursor.fetchall() 

    print ("ROWS:") 
    for row in rows: 
     print (" ", row) 

    conn.close() 

    return 

if __name__ == "__main__": 
    main() 
最初のテストデータベース&テーブルを生成し、説明するために

上記のスクリプトを端末で実行し、Control-Cを押す(または何らかの方法でSIGINTを送信する)場合、は最終的にというクエリとスクリプトを取り消しますが、少しの時間、多くの分。 sqlite3 command line toolで実行されているのとまったく同じクエリは、Control-Cを押すとすぐにキャンセルされます。

ありがとうございます!

+0

方法についてsqlite3.Connection.interrupt](https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.interrupt)? –

+0

@DaveJonesはい、質問の一番上に記載されているように、それを行う方法にする必要があります。しかし、どのように、実際のコードで?私は成功せずに試してきましたが、おそらくPythonの専門知識が不足しているため、私には明らかなことが分かりません。 – terse

+0

申し訳ありませんが、そのビットを逃しました - 私は今私の娘を迎えに行かなくてはなりませんが、ちょっと例を書いておきます( 'threading.Thread(target = conn.interrupt).start (それがあなたをその間に始められるようにするには十分です)。 –

答えて

0

私はそれを取り組んだと思うので、私自身の質問に答える。以下は私が思いついたものですが、このコードのコメントは非常に高く評価されます。

#!/usr/bin/env python 

from __future__ import print_function 
import sqlite3 
import threading 
import signal 
import os 
import time 

conn = None 
shutdown = False 

def main(): 
    global conn 

    # Long running query (pathological by design) 
    statement =''' 
SELECT DISTINCT a.id,a.rand 
FROM randint a 
    JOIN randint b ON a.id=b.rand 
    JOIN randint c ON a.id=c.rand 
    JOIN randint d ON a.id=d.rand 
    JOIN randint e ON a.id=e.rand 
    JOIN randint f ON a.id=f.rand 
    JOIN randint g ON a.id=g.rand 
    JOIN randint h ON a.id=h.rand 
ORDER BY a.id limit 10''' 

    conn = sqlite3.connect('randtable.sqlite', 10.0) 
    cursor = conn.cursor() 

    print ("Executing query") 

    try: 
     cursor.execute(statement) 
    except Exception as err: 
     if str(err) != "interrupted": 
      print ("Database error: {0}".format(str(err))) 
     return None 

    rows = cursor.fetchall() 

    print ("ROWS:") 
    for row in rows: 
     print (" ", row) 

    conn.close() 
    conn = None 

    return 

def interrupt(signum, frame): 
    global conn 
    global shutdown 

    print ("Interrupt requested") 

    if conn: 
     conn.interrupt() 

if __name__ == "__main__": 
    signal.signal(signal.SIGINT, interrupt) 

    mainthread = threading.Thread(target=main) 
    mainthread.start() 

    while mainthread.isAlive(): 
     time.sleep(0.2) 
2

あなたの答えは、それをカバーしていますが、(それは昨日の私の心を滑らせた後 - !申し訳ありません)私は答えを書くことを約束したのだ思い出したので、ここであなたがグローバルずにこれを行うことができます実証別のバージョンがあります。私はまた、信号の代わりにthreading.Eventを使用しました。これは、何かを行う時であることをスレッドに通知するいくつかの方法があることを実証するためです(しかし、あなたの目的のために、これはCtrl + Cに反応するのに最適です。

import sqlite3 
import time 
import threading 

# Background thread that'll kill our long running query after 1 second 
def kill_it(connection, event): 
    event.wait() 
    time.sleep(1) 
    connection.interrupt() 

# Make some tables with lots of data so we can make a long running query 
def big_query(conn, kill_event): 
    print('Making big tables') 
    conn.execute(
     "CREATE TABLE foo (i integer primary key, s text);") 
    conn.execute(
     "CREATE TABLE bar (j integer primary key, s text);") 
    conn.execute(
     "INSERT INTO foo VALUES %s" % ", ".join("(%d, 'foo')" % i for i in range(10000))) 
    conn.execute(
     "INSERT INTO bar VALUES %s" % ", ".join("(%d, 'bar')" % i for i in range(10000))) 
    kill_event.set() 
    print('Running query') 
    cur = conn.cursor() 
    cur.execute(
     "SELECT * FROM foo, bar") 
    print(len(cur.fetchall())) 

def main(): 
    conn = sqlite3.connect('foo.db') 
    kill_event = threading.Event() 
    kill_thread = threading.Thread(target=kill_it, args=(conn, kill_event)) 
    kill_thread.start() 
    big_query(conn, kill_event) 
    kill_thread.join() 

if __name__ == '__main__': 
    main() 
+0

優秀、代替のおかげで! – terse

関連する問題