def execute(self, query, args=None): """Executes a given query with given arguments.""" # When talking to MariaDB, MySQLdb code seems to have no problem # with sending multiple statements in a single cursor.execute() # call. MySQL, on the other hand, doesn't tolerate such behavior. # It returns "Commands out of sync" error. # # It seems that according to the official spec, cursor.execute() # can accept a single statement only. See: # https://www.python.org/dev/peps/pep-0249/#id15 # ".execute(): prepare and execute a database operation # (query or command)." # # And: # https://stackoverflow.com/questions/20518677/mysqldb-cursor-execute-cant-run-multiple-queries if ";" in query: # NOTE: query doesn't contain actual data (the template substitution # is done at a later stage), so there's practically no danger of a false # positive in this check. raise SemicolonNotAllowedInQueryError( "cursor.execute() can execute a single SQL statement only") try: result = self._forward(self.cursor.execute, query, args=args) if MySQLdb.version_info >= (1, 4, 0) and self.con.warning_count(): # Newer MySQLdb versions do not automatically turn MySQL warnings into # Python warnings, so this behavior must be implemented explicitly. for warning in self.con.show_warnings(): warnings.warn(MySQLdb.Warning(*warning[1:3]), stacklevel=3) return result except Warning as e: # TODO: check if newer versions of mysqlclient report # integrity errors as MySQLdb.IntegrityError exceptions and # not simply as warnings. # # MySQL error code 1452: Cannot add or update a child row: # a foreign key constraint fails if e.args[0] == 1452: raise MySQLdb.IntegrityError(str(e)) # TODO: check if newer versions of mysqlclient report the # unknown table warning (that's thrown even if DROP TABLE IF EXISTS # syntax is used, see # https://dev.mysql.com/doc/refman/5.7/en/drop-table.html) # # MySQL error code 1051: Unknown table. if e.args[0] == 1051: return None # TODO: check if newer versions of mysqlclient still report # the CONSTRAINT...FOREIGN KEY warning as a warning and not as an # integrity error. if (isinstance(e.args[0], str) and "CONSTRAINT" in e.args[0] and "FOREIGN KEY" in e.args[0]): raise MySQLdb.IntegrityError(str(e)) raise
def executemany(self, sql, param_list): try: return self.cursor.executemany(sql, param_list) except Database.Warning, w: self.cursor.execute("SHOW WARNINGS") raise Database.Warning("%s: %s" % (w, self.cursor.fetchall()))