IndexError: tuple index out of range

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
andyritter
User
Beiträge: 29
Registriert: Montag 7. März 2016, 16:27

Hallo zusammen,

Ich verarbeite eine Datenmenge, die aus einer Abfrage an einen anderen Server stammt und in JSON Format geliefert wird.

Hierbei filtere ich mir meine benötigten Daten heraus und lege mir diese in eine Variable. Den Wert der Variable möchte ich nun per Insert in eine PostgreSQL Datenbank per psycpg2 schreiben.

# Füllen der Variablen zur Zusammenfassung für die Übertragung zur Datenbank
i = 0
for item in m:
print('Kontrollpunkt' ,i)
osp_meters_project_id = m['project_id']
osp_meters_audit_timestamp = m['timestamp']

#for Item in m['resource_metadata']:
osp_meters_audit_period_beginning = m['resource_metadata']['audit_period_beginning']
osp_meters_audit_period_ending = m['resource_metadata']['audit_period_ending']
osp_meters_created_at = m['resource_metadata']['created_at']

if m['resource_metadata']['deleted_at']== '':
print('Kontrollpunkt empty')
osp_meters_deleted_at = m['timestamp']
else:
print('Kontrollpunkt Not empty')
osp_meters_deleted_at = m['resource_metadata']['deleted_at']

osp_meters_ephemeral_gb = m['resource_metadata']['ephemeral_gb']
osp_meters_root_gb = m[i]['resource_metadata']['root_gb']
Invoice_calculated = int(osp_meters_ephemeral_gb) + int(osp_meters_root_gb)
osp_meters_invoice_disk_gb = Invoice_calculated
osp_meters_display_name = m[i]['resource_metadata']['display_name']
osp_meters_instance_id = m[i]['resource_metadata']['instance_id']
osp_meters_instance_type = m[i]['resource_metadata']['instance_type']
osp_meters_instance_type_id = m[i]['resource_metadata']['instance_type_id']
osp_meters_memory_mb = m[i]['resource_metadata']['memory_mb']
osp_meters_state = m[i]['resource_metadata']['state']
osp_meters_tenant_id = m[i]['resource_metadata']['tenant_id']
osp_meters_vcpus = m[i]['resource_metadata']['vcpus']

print(osp_meters_project_id)
print(osp_meters_audit_period_beginning)
print(osp_meters_audit_period_ending)
print(osp_meters_created_at)
print(osp_meters_deleted_at)
print(osp_meters_ephemeral_gb)
print(osp_meters_root_gb)
print(osp_meters_invoice_disk_gb)
print( osp_meters_display_name)
print(osp_meters_instance_id)
print(osp_meters_instance_type)
print(osp_meters_instance_type_id)
print(osp_meters_memory_mb)
print(osp_meters_state)
print(osp_meters_tenant_id)
print(osp_meters_vcpus)
print(osp_meters_audit_timestamp)

try:
cur.execute("""INSERT INTO osp_meters (project_id, audit_period_beginning, audit_period_ending, created_at, deleted_at, display_name, invoice_disc_gb, root_gb, ephemeral_gb, memory_mb, vcpus, instance_id,instance_type, instance_type_id,tenant_id, audit_timestamp) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",(osp_meters_project_id,osp_meters_audit_period_beginning, osp_meters_audit_period_ending,osp_meters_created_at,osp_meters_deleted_at,osp_meters_display_name,osp_meters_invoice_disk_gb,osp_meters_root_gb, osp_meters_ephemeral_gb, osp_meters_memory_mb,osp_meters_vcpus, osp_meters_state,osp_meters_instance_id, osp_meters_instance_type,osp_meters_instance_type_id,osp_meters_audit_timestamp))
except psycopg2.IntegrityError:
print("oouups")
else:
print("alles ok")
i= i + 1
conn.commit()

Wenn ich mir die Veariablen per print ausgeben lasse, so sieht alles ok aus, der Insert jedoch Fällt mit folgender Meldung auf die Nase:

Traceback (most recent call last):
File "/home/akquinet/Dokumente/extract meters aus Liste.py", line 1011, in <module>
cur.execute("""INSERT INTO osp_meters (project_id, audit_period_beginning, audit_period_ending, created_at, deleted_at, display_name, invoice_disc_gb, root_gb, ephemeral_gb, memory_mb, vcpus, instance_id,instance_type, instance_type_id,tenant_id, audit_timestamp) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",(osp_meters_project_id,osp_meters_audit_period_beginning, osp_meters_audit_period_ending,osp_meters_created_at,osp_meters_deleted_at,osp_meters_display_name,osp_meters_invoice_disk_gb,osp_meters_root_gb, osp_meters_ephemeral_gb, osp_meters_memory_mb,osp_meters_vcpus, osp_meters_state,osp_meters_instance_id, osp_meters_instance_type,osp_meters_instance_type_id,osp_meters_audit_timestamp))
IndexError: tuple index out of range


Woran kann das noch liegen?
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@andyritter: bei 17 Parametern kann man nur den Überblick verlieren. Sowas möchte man nicht debuggen.

Code: Alles auswählen

FIELDS = ['audit_period_beginning', 'audit_period_ending', 'created_at', 
        'deleted_at', 'display_name', 'root_gb', 'ephemeral_gb',
        'memory_mb', 'vcpus', 'instance_id', 'instance_type', 'instance_type_id',
        'tenant_id']

for item in m:
    resource = item['resource_metadata']
    data = {key: resource.get(key) for key in FIELDS}
    data['project_id'] = item['project_id']
    data['audit_timestamp'] = item['timestamp']
    if not data['deleted_at']:
        print('Kontrollpunkt empty')
        data['deleted_at'] = data['audit_timestamp']
    else:
        print('Kontrollpunkt Not empty')
    data['invoice_disk_gb'] = int(data['ephemeral_gb']) + int(data['root_gb'])

    try:
        cur.execute("""INSERT INTO osp_meters (%s) VALUES (%s)"""
            % (data.keys(), ','.join(['%s']*len(data)),
            data.values()
        )
    except psycopg2.IntegrityError:
        print("oouups")
    else:
        print("alles ok")
conn.commit()
BlackJack

@miracle173: Das ist sehr wohl eine parametrisierte SQL-Anfrage die Sirius3 gezeigt hat. Schau da noch mal genauer hin. Da werden Platzhalter per Platzhalter eingefügt damit man die nicht per 17 mal tippen muss. Nur bei den Spaltennamen kann eventuell etwas passieren, je nach dem wo die Daten her kommen.

Ich würde ja eine Abstraktionsschicht wie SQLAlchemy verwenden. :-)
Benutzeravatar
miracle173
User
Beiträge: 127
Registriert: Samstag 6. Februar 2016, 00:28

BlackJack hat geschrieben:@miracle173: Das ist sehr wohl eine parametrisierte SQL-Anfrage die Sirius3 gezeigt hat. (...)
Danke, da habe ich mich tasächlich verschaut. Na wenigstens bin ich so meine Weisheiten über parametrisierte Abfragen los geworden.
Benutzeravatar
miracle173
User
Beiträge: 127
Registriert: Samstag 6. Februar 2016, 00:28

andyritter hat geschrieben: (Anmerkung: Ich habe die Löschung des ursprünglichen Beitrags beantragt. Hier ist der Rest, der verbleiben soll, noch einmal geposted)

Code: Alles auswählen

(...)
        cur.execute("""INSERT INTO osp_meters (project_id, audit_period_beginning, audit_period_ending, created_at, deleted_at, display_name, invoice_disc_gb, root_gb, ephemeral_gb, memory_mb, vcpus,  instance_id,instance_type, instance_type_id,tenant_id, audit_timestamp) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",(osp_meters_project_id,osp_meters_audit_period_beginning, osp_meters_audit_period_ending,osp_meters_created_at,osp_meters_deleted_at,osp_meters_display_name,osp_meters_invoice_disk_gb,osp_meters_root_gb, osp_meters_ephemeral_gb, osp_meters_memory_mb,osp_meters_vcpus, osp_meters_state,osp_meters_instance_id, osp_meters_instance_type,osp_meters_instance_type_id,osp_meters_audit_timestamp))
(...)
Ich würde das eher so formatieren, wobei ich nicht ausprobiert habe, ob das in Python überhaupt gültig ist. '--' ist ein SQL Kommentarzeichnen.

Code: Alles auswählen

(...)

        cur.execute("""INSERT INTO osp_meters (
        	project_id, 
        	audit_period_beginning, 
        	audit_period_ending, 
        	created_at, 
        	deleted_at,          -- 5
        	display_name,   
        	invoice_disc_gb, 
        	root_gb, 
        	ephemeral_gb, 
        	memory_mb,        --10
        	vcpus,
        	instance_id,
        	instance_type, 
        	instance_type_id,
        	tenant_id,             --15        	
        	audit_timestamp
        		) VALUES (
        	%s,
        	%s, 
        	%s, 
        	%s, 
        	%s,      --10
        	%s, 
        	%s, 
        	%s, 
        	%s, 
        	%s,      --10
        	%s, 
        	%s, 
        	%s, 
        	%s, 
        	%s,      --15
        	%s, 
        	%s
        	 	)""",(
        	osp_meters_project_id,
        	osp_meters_audit_period_beginning,
        	osp_meters_audit_period_ending,
        	osp_meters_created_at,
        	osp_meters_deleted_at,           # 5
        	osp_meters_display_name,
        	osp_meters_invoice_disk_gb,
        	osp_meters_root_gb, 
        	osp_meters_ephemeral_gb, 
        	osp_meters_memory_mb,         #10
        	osp_meters_vcpus, 
        	osp_meters_state,
        	osp_meters_instance_id, 
        	osp_meters_instance_type,
        	osp_meters_instance_type_id,   #15 
        	osp_meters_audit_timestamp
        	)
        )
(...)
Da fällt nun sofort auf, dass diene Values-Klausel 17 %s hat , Obwohl nur 16 Spalten angeführt sind (und auch nur 16 Parameter). Das ist der Grund für deinen IndexError: tuple index out of range.
andyritter
User
Beiträge: 29
Registriert: Montag 7. März 2016, 16:27

Vielen Dank für eure Hilfe

@'Sirius3: Bei Deinem Code fehlte noch eine )

Code: Alles auswählen

try:
        cur.execute("""INSERT INTO osp_meters (%s) VALUES (%s)"""
            % (data.keys(), ','.join(['%s']*len(data)),
            data.values()
            )
        )
    except psycopg2.IntegrityError:
und danach hatte ich die Meldung, dass nicht alle Daten in einen String umgewandelt werden konnten. Daher habe ich dieses Snippet nicht weiter verwendet.

@miracle173: ganz nach Deinem Vorschlag die Variablen und die Felder einmal aufzulisten habe ich entdeckt, dass ein Feld untergegangen war. Danke für diesen Tipp, manchmal ist das naheliegenste einfach nicht mehr zu sehen (Wald vor lauter Bäumen ... usw.)

schöne Ostern euch allen
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@andyritter: Wo Du die fehlende Klammer hingesetzt hast, zeigt, dass Du Dich nicht mit dem Code auseinandergesetzt hast.

Code: Alles auswählen

        cur.execute("""INSERT INTO osp_meters (%s) VALUES (%s)"""
            % (','.join(data.keys()), ','.join(['%s']*len(data))),
            data.values()
        )
Als Programmierer versucht man, Wiederholungen möglichst zu vermeiden, weil das nur eine Quelle für Fehler ist. Wie Du vielleicht siehst, kommen die meisten Feldnamen nur einmal vor, bei Dir jeder mindestens 4 mal. Wie BlackJack schon angedeutet hat, ist es hier empfehlenswert ein ORM zu verwenden, wie es SQLAlchemy anbietet. Dann muß man sich erst gar nicht damit herum schlagen, selbst SQL-Statements zusammenzubauen.

@miracle173: Da die Kontrolle darüber, was in den SQL-String hineinformatiert wird, komplett beim Programmierer liegt, kann es keine Angriffsmöglichkeit geben. Problematisch wird es nur, wenn tatsächlich User-Eingaben im Statement verarbeitet werden. Hier sind die User-Eingaben aber über den zweiten Parameter von execute getrennt vom SQL-Statement. Falls irgendwer etwas cachen will, kann er das tun, weil innerhalb der for-Schleife immer der selbe SQL-String erzeugt wird. SQLAlchemy macht intern auch nichts anderes.
Benutzeravatar
miracle173
User
Beiträge: 127
Registriert: Samstag 6. Februar 2016, 00:28

Sirius3 hat geschrieben: @miracle173: Da die Kontrolle darüber, was in den SQL-String hineinformatiert wird, komplett beim Programmierer liegt, kann es keine Angriffsmöglichkeit geben. Problematisch wird es nur, wenn tatsächlich User-Eingaben im Statement verarbeitet werden. Hier sind die User-Eingaben aber über den zweiten Parameter von execute getrennt vom SQL-Statement. Falls irgendwer etwas cachen will, kann er das tun, weil innerhalb der for-Schleife immer der selbe SQL-String erzeugt wird. SQLAlchemy macht intern auch nichts anderes.
Ich habe nämlich geschrieben:
miracle173 hat geschrieben: Das ist im Allgemeinen keine geignete Lösung. Obwohl das dem Statement von @andyritter ähnelt, sogar eleganter ausschaut, ist das keine parameterisiertee Abfrage. Das cur.execute Statement enthält nur ein Arguement, nämlich den Statement-String, der keinerlei Parameter, also %s Zeichenketten, enthält. Die im urspürngliche Staement vorkommenden %s Symbole werden schon im Rahmen des printf-style string formattting ersetzt. An die Datenbank wird dann ein String ohne Parameter geschickt.
Das war Irrtum von mir. Natürlich ist dein cur.execute Statement völlig korrekt and enthält zwei Argumente. Es ist ein parameterisiertes Statement und damit sind meine weiteren Anmerkungen völlig bedeutungslos für dein Satement.


Allerdings sollte man die Gefahr von sql-Injection nicht unterschätzen: http://www.lachschon.de/item/94804-Radarinjection/
Antworten