Im vorherigen Artikel Ich habe versucht, mit pypyodbc schnell Daten von AS / 400 abzurufen können Sie jetzt eine ODBC-Verbindung zu IBM i herstellen. Also habe ich mir viele Dinge ausgedacht, die ich tun wollte, aber als ich darüber nachdachte, musste ich sie vorbereiten, also habe ich dieses Mal dieses Gebiet geschaffen.
Der Grund, warum es "Nr. 1" ist, ist, dass es sich noch mitten auf der Straße befindet und nur der Eingabeteil verarbeitet werden kann. Die Ausgabe ist ohne die tatsächliche Maschine schwer zu überprüfen, daher ist sie diesmal vergangen.
Ich denke, ich werde es später verbrauchen, also werde ich verschiedene Dinge in eine Datei (Modul) namens "sql_helper.py" packen. Es ist eine grundlegende Funktion, die Sie möglicherweise finden, wenn Sie nach einem Paket suchen, aber ich habe das Rad als Überprüfung von Python neu erfunden.
Es sieht vorerst so aus.
sql_helper.py
def _load_lines(filename, encoding='utf-8'):
return [ l for l in open(filename, 'r', -1, encoding) ]
def load_raw_string(filename, encoding='utf-8', rstrip=True, lstrip=False):
def _echo(x): return x
if ( rstrip and lstrip): func = str.strip
elif (not rstrip and lstrip): func = str.lstrip
elif ( rstrip and not lstrip): func = str.rstrip # default
else: func = _echo # dummy
if rstrip: term = "\n" # cause rstrip(None) is remove "\n"!
else: term = ""
s = _load_lines(filename, encoding)
return term.join( list(map(lambda x: func(x), s)) )
def load_sql(filename, encoding='utf-8', lstrip=False):
sts = []
for st in strip_comment( load_raw_string(filename, encoding, True, lstrip) ).split(";\n"):
if (st.isspace() or st == "" ): continue
sts.append(st.strip("; \t\n") )
return sts
def load_config(filename, encoding='utf-8'):
cf = {}
for line in strip_comment(load_raw_string(filename, encoding)).split("\n"):
if ( line == "" or line.isspace() ): continue
key_, val_ = line.split("=")
cf[ key_.strip() ] = val_.strip()
return cf
# ...Wird etwas länger dauern...
Ich werde es kurz erklären.
_load_lines()
Lesen Sie die angegebene Datei. Erstellen Sie einfach eine Liste mit 1 Zeile, 1 Element und fertig. Ich möchte einfach nicht jedes Mal `open (xxx, ...). Readline ()`
schreiben.
load_raw_string()
Die mit _load_line ()
`erhaltene Liste wird gelesen, für jede Zeile mit lstrip () und rstrip () multipliziert und dann in eine einzelne Zeichenfolge rekonstruiert und zurückgegeben.
Sollte ich auswählen können, ob die Leerzeichen am Zeilenanfang und die Leerzeichen am Zeilenende gelöscht werden sollen? Nach einer einfachen Entscheidung wurde der Code größer. Vor _load_lines () haben wir die Funktion hinzugefügt. (Es wird auch gesagt, dass ich die return-Anweisung in einer Zeile beenden möchte)
Funktion hängt vom Parameter ab, str.lstrip、str.rstrip、str.strip、_echo(Geben Sie einfach den Eingabewert zurück)Wird ersetzt.
Wenn Sie str.rstrip und str.strip ohne Argumente aufrufen, verschwinden auch die Zeilenumbrüche am Ende der Zeile. Wird mit einer return-Anweisung eingegeben und rekonstruiert.
load_sql()
Holen Sie sich die SQL aus dem Dateinamen. Wenn ";" (+ Zeilenumbruch) darin enthalten ist, teilen Sie es dort und kehren Sie es in die Liste der SQL-Anweisungen zurück, und Sie sind fertig.
Es war gut, bis ich dachte, aber um dies zu realisieren, machte ich Helfer für Helfer.
Zum Beispiel wäre es schlecht, wenn es im Kommentar durch "; \ n" abgeschnitten würde, oder? Oder es wäre schlecht, wenn es durch das "; \ n" im Zitat abgeschnitten würde, oder? Oder so.
Wenn es sich im zusammengesetzten Fall um `` `...; / * Blockkommentar * /` `handelt, stimmt es nicht mit"; \ n "überein, wenn Sie das Leerzeichen am Ende der Zeile nach dem Entfernen des Kommentars erneut entfernen müssen! Und so weiter....
Was ich endlich mache, ist so
1. Entfernen Sie Leerzeichen am Ende der Zeile mit load_row_string ()
2. Entfernen Sie den Kommentar mit strip_comment () (später beschrieben) und entfernen Sie dann das Leerzeichen am Ende der Zeile, um den Kommentar zu entfernen.
3. Teilen Sie zuletzt mit "; \ n" in eine einzelne SQL-Anweisung auf.
4. Entfernen Sie das ";" selbst am Ende jedes Satzes
5. 4. neu verpacken und zurücksenden
Es wäre möglicherweise schneller gewesen, einen einfachen SQL-Analysator normal zu erstellen.
load_config()
Wenn Sie eine Datei im folgenden Format lesen ...
#### **`sample_connection.txt`**
```text
/* supports sql style comments */
driver = {iSeries Access ODBC Driver}
system = 127.0.0.1
uid = USER01
pwd = USER01
DefaultLibraries = MYLIB,TESTLIB1,TESTLIB2library
Konvertieren Sie in ein Wörterbuch wie dieses.
{'DefaultLibraries': 'MYLIB,TESTLIB1,TESTLIB2',
'driver': '{iSeries Access ODBC Driver}',
'pwd': 'USER01',
'system': '127.0.0.1',
'uid': 'USER01'}
Wenn Sie dieses Wörterbuch an die Funktion pypyodbc.connect () übergeben, müssen Sie keine Parameter hintereinander schreiben.
odbctest.py
from pypyodbc import connect
import sql_helper as helper
config = helper.load_config( "sample_connection.txt" )
with connect( **config ) as connection:
#Zugriffsverarbeitung auf Datenbank...
strip_comment() Wenn die Quelle länger wird, wird sie zu einer Funktion zum Entfernen von Kommentaren.
sql_helper.py
from enum import Enum, auto
class States(Enum):
Statement = auto()
Quoted = auto()
Comment = auto()
End = auto()
_LimitLen = 999999999
def _singlequote_begin(s, b):
p = s.find("'", b)
return p if (p >= 0) else _LimitLen
def _singlequote_end(s, b):
p = s.find("'", b)
if (p >= 0 and p == s.find("''", b)): p = _singlequote_end(s, p + 2) # find recursive
return (_next, p, 0, States.Statement) if (p >= 0) else (None. _LimitLen, 0, States.End)
def _doublequote_begin(s, b):
p = s.find('"', b)
return p if (p >= 0) else _LimitLen
def _doublequote_end(s, b):
p = s.find('"', b)
return (_next, p, 0, States.Statement) if (p >= 0) else (None, _LimitLen, 0, States.End)
def _block_comment_begin(s, b):
p = s.find("/*", b)
return p + 1 if (p >= 0) else _LimitLen
def _block_comment_end (s, b):
p = s.find("*/", b)
return (_next, p + len("*/") - 1, len("*/") -1, States.Statement) if (p >= 0) else (None, _LimitLen, 0, States.End)
def _line_comment_begin(s, b):
p = s.find("--", b)
return p + 1 if (p >= 0) else _LimitLen
def _line_comment_end(s, b):
p = s.find("\n", b)
return (_next, p + len("\n") - 1, len("\n") - 1, States.Statement) if (p >= 0) else (None, _LimitLen, 0, States.End)
def _quote_begin(s, b):
next_state = States.Quoted
sq, dq = _singlequote_begin(s, b), _doublequote_begin(s, b)
if (min(sq, dq) == _LimitLen): next_state = States.End
return (_singlequote_end, sq, 0, next_state) if (sq < dq) else (_doublequote_end, dq, 0, next_state)
def _comment_begin(s, b):
bc, lc = _block_comment_begin(s, b), _line_comment_begin(s, b)
if (min(bc, lc) == _LimitLen): next_ = States.End
return (_line_comment_end, lc, 0, States.Comment) if (lc < bc) else (_block_comment_end, bc, 0, States.Comment)
def _next(s, b):
q_func, q_pos, q_ad, q_st = _quote_begin(s, b)
c_func, c_pos, c_ad, c_st = _comment_begin(s, b)
return (q_func, q_pos, 0, q_st) if (q_pos < c_pos) else (c_func, c_pos, 0, c_st)
def strip_comment( st ):
#Kurze Bewertung
if st == None or len( st.strip() ) == 0: return ""
if ("/*" not in st) and ("--" not in st): return "\n".join( list ( map(lambda x: x.rstrip(), st.split("\n"))))
chars = list( st )
comments = _find_comment_pos( st )
comments.reverse()
for c in comments: del chars[ c[0]: c[1]]
return "\n".join( list( map(lambda x: x.rstrip() , "".join( chars ).split("\n")) ) )
def _find_comment_pos( st ):
cur = -1
begin = 0
comments = []
state = States.Statement
pstate = None
func = _next
while (True):
func, pos, adv, state = func(st, cur + 1)
#Suche Ende
if ( state == States.End):
if (pstate == States.Comment):
comments.append((begin, len(st)))
break
cur = pos
#Beginnen Sie mit dem Kommentieren->Überspringen Sie die nachfolgende Verarbeitung
if (state == States.Quoted):
pstate = state
continue
# end comment
if (pstate == States.Comment):
comments.append((begin, pos + adv))
pstate = state
continue
# begin comment
if (state == States.Comment):
begin = pos - 1 # previous a length of single/double quotation
pstate = state
return comments
Ich erinnere mich nicht mehr an das, was ich geschrieben habe, deshalb erkläre ich es kurz.
Strip_comment () und die Funktion, die die Kommentarposition durchsucht und eine Liste von (Startposition, Endposition) _find_comment_pos () zurückgibt. Die Hauptfunktionen sind die Funktionen und Objekte, die zuvor in einer Reihe angeordnet sind. Dies ist der von _find_comment_pos () verwendete Helfer.
Die verschiedenen Funktionen wurden ursprünglich als interne Funktionen von _find_comment () definiert, aber auf diese Weise
func, pos, adv, state = func(st, cur + 1)
Wenn Sie ausführen, wird die erste Funktion(Die nächste zu suchende Funktion wird zurückgegeben)Es scheint jedoch, dass es sich nicht um ein normales Funktionsobjekt handelt, sondern um einen dreiteiligen Taple.
Es scheint schwierig zu sein, herauszufinden, wie man damit umgeht, deshalb wechsle ich gehorsam zu einer normalen Funktion. (Aus diesem Grund habe ich beschlossen, zusätzliche Funktionen in das Modul zu streuen ...)
___find_comment___pos() Lokale Variablen'''func```Enthält die Funktion, die für die nächste Suche verwendet werden soll. Aktueller Status und Trefferzeichen("/**"Und"'"Und改行Und)Dies kann durch Teilen des Falls mit realisiert werden, aber da die bedingte Verzweigung schrecklich zu sein scheint, versuche ich, eine geeignete Suchfunktion entsprechend dem Treffercharakter zurückzugeben. Beispiel) "/"Wenn Sie finden, das nächste, wonach Sie suchen müssen"/"Da ist entschieden, die Funktion, danach zu suchen_block_comment_end()Ist entschieden
Reicht es zu diesem Zeitpunkt aus, die Trefferposition zusammen zurückzugeben? Ich dachte, aber nachdem ich diese Informationen haben möchte, brauche ich diese auch. Ich muss 4 Werte von jeder Funktion zurückgeben...。 Danach habe ich den numerischen Wert so angepasst, dass die Kommentarposition durch Slice angegeben werden kann, und sie in die Liste gepackt. (Ich habe die Details vergessen..)
Nachdem die Position des Kommentars festgelegt wurde, müssen Sie den Kommentar nur noch mit einem Slice entfernen! !!
Die String-Klasse hat keine Methode, um das Zeichen an der Slice-Position zu entfernen! !!
Es kann nicht geholfen werdenchars = list( st )
Konvertieren Sie in eine Liste von Zeichen mitdel chars[begin:end]
Auf diese Weise gelang es mir schließlich, den Kommentar zu entfernen, nicht die lang erwartete Eissäge d.
Die letzte return-Anweisung, aber die leere Zeile am Ende der Zeile nach dem Entfernen des Kommentars("Text; /Kommentar/\n"Muster)Ich beschloss, erneut einen Kreisverkehr zu schreiben, um ihn weiter zu löschen.
#Schließlich Die für den Test verwendete Datei und der Testcode.(sample_connection.txt)Wird weggelassen, da es bereits gebucht wurde)
sample_multi_statement.sql
select * from SYSLIBL
where TYPE not like = "';%" ;/* comment */
select * from SYSTABLES ; /* "" ' '' ; ;
" */
; -- empty statement
select * from SYSCOLUMNS ;
select * from SYSIBM.SYSDUMMY1;
strip_Zur Bestätigung der Kurzschlussauswertung des Kommentars
sample_multi_statement.sql
select * from SYSLIBL
where TYPE not like = "';%" ;
select * from SYSTABLES ;
;
select * from SYSCOLUMNS ;
select * from SYSIBM.SYSDUMMY1;
sql_helper.py
def test_loading():
from pprint import pprint
file = "sample_multi_statement.sql"
print( f'\ntest load_sql("{file}") ----------------------------')
sts = load_sql( file )
pprint( sts )
file = "sample_multi_statement2.sql"
print( f'\ntest load_sql("{file}") ----------------------------')
sts = load_sql( file )
pprint( sts )
file = "sample_config.txt"
print( f'\ntest load_config("{file}") ----------------------------')
config = load_config( file )
pprint( config )
Die Menge an Code war größer als erwartet, nur durch Lesen. Ich habe mit der Erstellung unter der Annahme begonnen, dass es mit IBM i verwendet wird, aber ich denke, dass der bisherige Inhalt als Vorverarbeitung für andere DBMS verwendet werden kann.
Nächstes Mal werden wir auf der Ausgabeseite beginnen.
Recommended Posts