2015-02-16 8 views
5

Ich versuche, einen MySql-Treiber zu finden, den ich mit Go verwenden kann, das die Ausgabe mehrerer SQL-Anweisungen in einem Aufruf unterstützt. Zum Beispiel könnte ich wünsche eine Datenbank mit dem folgende SQL zu erstellen:Existiert ein Go Mysql-Treiber, der mehrere Anweisungen innerhalb einer einzelnen Zeichenfolge unterstützt?

DROP SCHEMA IF EXISTS foo; 
CREATE SCHEMA IF NOT EXISTS foo; 

In Sprachen wie PHP können Sie nur die beiden SQL-Anweisungen in einer Zeichenfolge platzieren und es in einem Rutsch ausführen, wie folgt aus:

$db = new PDO(...); 
$db->query("DROP SCHEMA IF EXISTS foo; CREATE SCHEMA IF NOT EXISTS foo;"); 

Der Grund, warum ich das brauche, ist, weil ich SQL-Dumps habe (von mysqldump) Ich möchte programmgesteuert auf verschiedene Datenbanken anwenden.

Ich bin auf der Suche nach der gleichen Funktionalität in Go, aber es scheint, dass die verschiedenen Treiber es nicht unterstützen, was, offen gesagt, ist schockierend für mich.

Go-MySQL-Treiber
https://github.com/go-sql-driver/mysql
Dies scheint die am häufigsten verwendeten Treiber für Go zu sein.

package main 

import "database/sql" 
import "log" 
import _ "github.com/go-sql-driver/mysql" 

func main() { 

    db, err := sql.Open("mysql", "user:[email protected](127.0.0.1:3306)/") 
    if err != nil { 
     log.Println(err) 
    } 

    sql := "DROP SCHEMA IF EXISTS foo; CREATE SCHEMA IF NOT EXISTS foo;" 
    _, err = db.Exec(sql) 
    if err != nil { 
     log.Println(err) 
    } 

    db.Close() 
} 

Ausgang:

2015/02/16 18:58:08 Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CREATE SCHEMA IF NOT EXISTS foo' at line 1 

MyMySQL
https://github.com/ziutek/mymysql
Dies ist ein weiterer beliebter Fahrer.

package main 

import "database/sql" 
import "log" 
import _ "github.com/ziutek/mymysql/godrv" 

func main() { 

    db, err := sql.Open("mymysql", "database/user/password") 
    if err != nil { 
     log.Println(err) 
    } 

    sql := "DROP SCHEMA IF EXISTS foo; CREATE SCHEMA IF NOT EXISTS foo;" 
    _, err = db.Exec(sql) 
    if err != nil { 
     log.Println(err) 
    } 

    sql = "USE DATABASE foo;" 
    _, err = db.Exec(sql) // <-- error 
    if err != nil { 
     log.Println(err) 
    } 

    db.Close() 
} 

Ausgang:

2015/02/16 18:58:08 packet sequence error 

Kennt jemand jeden MySQL-Treiber kompatibel mit Go, die mehrere Anweisungen in einer Zeichenfolge wie diese verarbeiten kann?

+1

Go MySQL Driver hat jetzt Multi-Statement-Unterstützung: https://github.com/go-sql-driver/mysql/pull/411 –

Antwort

0

https://github.com/ziutek/mymysql

kann es tun. Obwohl Sie seine Schnittstelle gegen die gehen müssen, die definiert wird. Die offizielle Schnittstelle geht nicht damit um, oder mehrere Rückgabewerte.

package main 

import (
    "flag" 
    "fmt" 

    "github.com/ziutek/mymysql/autorc" 
    "github.com/ziutek/mymysql/mysql" 
    _ "github.com/ziutek/mymysql/thrsafe" 
) 

type ScanFun func(int, []mysql.Row, mysql.Result) error 

func RunSQL(hostport, user, pass, db, cmd string, scan ScanFun) error { 
    conn := autorc.New("tcp", "", hostport, user, pass, db) 

    err := conn.Reconnect() 
    if err != nil { 
     return err 
    } 

    res, err := conn.Raw.Start(cmd) 
    if err != nil { 
     return err 
    } 

    rows, err := res.GetRows() 
    if err != nil { 
     return err 
    } 

    RScount := 0 
    scanErr := error(nil) 

    for { 
     if scanErr == nil { 
      func() { 
       defer func() { 
        if x := recover(); x != nil { 
         scanErr = fmt.Errorf("%v", x) 
        } 
       }() 
       scanErr = scan(RScount, rows, res) 
      }() 
     } 

     if res.MoreResults() { 
      res, err = res.NextResult() 
      if err != nil { 
       return err 
      } 
      rows, err = res.GetRows() 
      if err != nil { 
       return err 
      } 
     } else { 
      break 
     } 

     RScount++ 
    } 
    return scanErr 
} 

func main() { 
    host := flag.String("host", "localhost:3306", "define the host where the db is") 
    user := flag.String("user", "root", "define the user to connect as") 
    pass := flag.String("pass", "", "define the pass to use") 
    db := flag.String("db", "information_schema", "what db to default to") 

    sql := flag.String("sql", "select count(*) from columns; select * from columns limit 1;", "Query to run") 

    flag.Parse() 

    scan := func(rcount int, rows []mysql.Row, res mysql.Result) error { 
     if res.StatusOnly() { 
      return nil 
     } 

     for idx, row := range rows { 
      fmt.Print(rcount, "-", idx, ") ") 
      for i, _ := range row { 
       fmt.Print(row.Str(i)) 
       fmt.Print(" ") 
      } 
      fmt.Println("") 
     } 
     return nil 
    } 

    fmt.Println("Host - ", *host) 
    fmt.Println("Db - ", *db) 
    fmt.Println("User - ", *user) 

    if err := RunSQL(*host, *user, *pass, *db, *sql, scan); err != nil { 
     fmt.Println(err) 
    } 
} 
+0

Das scheint tatsächlich zu funktionieren! Vielen Dank. –

-3

Bluntly setzen, tun nicht mehrere Anweisungen in einem einzigen Aufruf ausgeben. Es ist ein Sicherheitsproblem. Wenn ein Hacker ein wenig eindringen kann, kann er Ihre Maschine übernehmen. (Lesen Sie "SQL Injection".)

Gespeicherte Routinen bieten Ihnen eine sichere Möglichkeit, Anweisungen zu kombinieren (mit einigen Einschränkungen).

+3

Es ist kein Sicherheitsproblem und Sie haben offensichtlich die Frage nicht gelesen. Ich habe es nicht mit parametrisierten Abfragen zu tun, ich versuche, intern gestaltete DDLs auf das vorhandene Schema anzuwenden. SQL Injection ist hier nicht einmal ein Problem. –

+0

Es scheint, dass die Antwort nicht verwandt ist, aber in Anbetracht der Tatsache, dass die Treiber mehrere Anweisungen unterstützen, wenn multiStatements = true zu den Verbindungsparametern hinzugefügt wird und die Dokumentation sagt 'Erlaube mehrere Anweisungen in einer Abfrage. Dies ermöglicht Batch-Abfragen, erhöht jedoch auch das Risiko von SQL-Injektionen erheblich. Nur das Ergebnis der ersten Abfrage wird zurückgegeben, alle anderen Ergebnisse werden stillgelegt. Es ist ein relevanter Kommentar, obwohl er vielleicht nicht korrekt formuliert ist. – ithkuil

1

Ich würde empfehlen, einfach 2 Anrufe zu machen. Warum nicht? Es erleichtert den Code und verbessert die Fehlerbehandlung.

Die andere Option, wenn Sie eine große SQL-Datei aus dem Dump haben, ist Shell und führen Sie die ganze Sache in einem Rutsch aus.

+1

Das Ausführen mehrerer Aufrufe ist keine realistische Option, da ich den SQL-Dump analysieren müsste, um die Anweisungen zu trennen. Die Verwendung der Shell wäre möglich, aber das Programm ist nicht plattformübergreifend. –

10

Die github.com/go-sql-driver/mysql kann so konfiguriert werden, dass mehrere Anweisungen mit dem Verbindungsparameter multiStatements=true akzeptiert werden.

Die Dokumentation zeigt deutlich, warum Sie vorsichtig sein sollten. Siehe https://github.com/go-sql-driver/mysql

+0

Kennen Sie einen Beispielcode für die Syntax der Verwendung von Mehrfachanweisungen? – Sir

0

Hinzufügen eines Beispiels für die Antwort von @ithkuil in Bezug auf Multistatements für das Go-Sql-Treiberpaket als Referenz. (Ich hatte nicht genug Rep als Kommentar hinzufügen).

Der Parameter für multiStatements wird der dataSourceName-Zeichenfolge für Ihren sql.Open-Aufruf hinzugefügt. z.B.

db, err := sql.Open("mysql", "user:[email protected](localhost:3306)/dbname?multiStatements=true") 

Es wird empfohlen, dass Sie verwenden eine solche db-Handler für die Verarbeitung von Benutzereingaben nicht, aber es funktioniert gut für die Verarbeitung bekannten SQL-Dateien.

Verwandte Themen