emma-0.6/ 0000755 0001750 0001750 00000000000 10526165411 010421 5 ustar flo flo emma-0.6/emmalib/ 0000755 0001750 0001750 00000000000 10526165411 012027 5 ustar flo flo emma-0.6/emmalib/plugins/ 0000755 0001750 0001750 00000000000 10526165411 013510 5 ustar flo flo emma-0.6/emmalib/plugins/pretty_format/ 0000755 0001750 0001750 00000000000 10526165411 016407 5 ustar flo flo emma-0.6/emmalib/plugins/pretty_format/__init__.py 0000644 0001750 0001750 00000025665 10516754317 020546 0 ustar flo flo # -*- coding: utf-8 -*-
# emma
#
# Copyright (C) 2006 Florian Schmidt (flo@fastflo.de)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
import sys
import time
import gtk.glade
import gobject
import pprint
import re
import cStringIO
def test(c, t, f):
if c:
return t
return f
def strspn(text, accept, pos=0):
tl = len(text)
start = pos
while pos < tl and text[pos] in accept:
pos += 1
return pos - start
def strcspn(text, reject, pos=0):
tl = len(text)
start = pos
while pos < tl and text[pos] not in reject:
pos += 1
return pos - start
def search_string_end(text, delim, p):
s = p
while True:
l = strcspn(text, delim, s)
# check if there is an odd count of backslashes before
r = s + l - 1
count = 0
while text[r] == "\\":
r -= 1
count += 1
escaped = count % 2 == 1
if not escaped:
break
s = s + l
return s + l
def search_parenthesis_end(text, s):
# search closing ) jumping over quoted strings
opens = 1
s += 1
while True:
#print "search closing: %r" % text[s:]
el = strcspn(text, "()\"'", s)
end = s + el
print "ptoken: %r" % text[end]
if text[end] == "(":
opens += 1
#print "now open:", opens
s = end + 1
continue
if text[end] == ")":
opens -= 1
#print "now open:", opens
s = end + 1
if opens == 0:
break
continue
# must be a string
delim = text[end]
strend = search_string_end(text, delim, end + 1)
quoted_string = text[end:strend + 1]
#print "quoted string: %r" % quoted_string
s = strend + 1
return s
def get_token(text, p, allow_functions=False):
ttype = "token"
o = strspn(text, " \r\n\t", p)
p = p + o
print "\nget token from %r" % text[p:p + 25]
if not text[p:]:
return ttype, "", len(text)
if text[p] in "\"'":
delim = text[p]
end = search_string_end(text, delim, p + 1)
quoted_string = text[p:end + 1]
#print "quoted string: %r" % quoted_string
ttype = "quoted string"
return ttype, quoted_string, end + 1
l = strcspn(text, " \r\n\t(,;=", p)
s = p + l
if s >= len(text):
print "********last token? : %r" % text[p:] # todo?
return ttype, text[p:], len(text)
print "found first %r" % text[s]
if text[s] == "(":
s = search_parenthesis_end(text, s)
if l == 0:
ttype = "parenthesis block"
else:
ttype = "function call"
l = s - p
elif text[s] in "=,;" and l == 0:
l = 1
else:
pp = p + l
wl = strspn(text, " \r\n\t", pp)
pp += wl
print "first char after: %r" % text[pp]
if text[pp] == "(":
# found function call
# read function arguments to this token
s = search_parenthesis_end(text, pp)
l = s - p
ttype = "function call"
token = text[p:p + l]
return ttype, token, p + l
def pretty_print_function_call(function, compressed=False):
l = strcspn(function, "(\n\r\t ")
function_name = function[:l]
ll = strspn(function, "\n\r\t ", l)
function_args = function[l + ll + 1:-1]
print "function name: %r args: %r" % (function_name, function_args)
out = function_name + "("
p = 0
tl = len(function_args)
while p < tl:
tt, token, e = get_token(function_args, p)
if not token:
break
print "fcn pf: token: %r type: %s" % (token, tt)
if tt == "function call":
# uniform pretty print a function call
token = pretty_print_function_call(token)
if not compressed and token == ",":
out += token + " "
else:
out += token
p = e
return out + ")"
class pretty_format:
def __init__(self, emma_instance):
self.emma = emma_instance
self.toolbar_items = []
self.plugin_dir = os.path.dirname(os.path.abspath(__file__))
self.install_toolbar_item("query_toolbar", gtk.STOCK_INDENT, "pretty format query", self.on_pretty_format)
self.install_toolbar_item("query_toolbar", gtk.STOCK_UNINDENT, "compress query", self.on_compress)
q = self.emma.current_query
if sys.stdout.debug:
# check if we are running with debug output - enable example text
print "\n\n\n"
self.set_query_text(q, """# this is the pretty format test query. click the "pretty format" or "compress query" button in the query-toolbar.
\n \t# comment before\n\n/* also before... */\n \tselect date_format \n\t (\nnow( \n"lalala" ) , "%Y-%m-%d" \n), ("%Y,((%m"), \', from ),here\', * from record_job
where some_field
=
"a very interesting 'text'"
order by job_id desc,
\tvalid_from,\n\t\t\n\tmode,\n\tquery,\n\tpriority desc limit 150;
select * from user;
""")
def cleanup(self):
for item, toolbar in self.toolbar_items:
toolbar.remove(item)
del item
def install_toolbar_item(self, toolbar_name, stock, item_catpion, callback):
toolbar = self.emma.xml.get_widget(toolbar_name)
button = gtk.ToolButton(stock)
button.set_label(item_catpion)
button.connect("clicked", callback)
button.set_tooltip(self.emma.tooltips, item_catpion)
toolbar.insert(button, -1)
button.show()
self.toolbar_items.append((button, toolbar))
def get_query_text(self, q):
buffer = q.textview.get_buffer()
return buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
def set_query_text(self, q, text):
buffer = q.textview.get_buffer()
buffer.set_text(text)
def on_pretty_format(self, button):
q = self.emma.current_query
text = self.get_query_text(q)
print "input: %r" % text
output = cStringIO.StringIO()
keyword_normalisation = "u"
def starts_with(t, s, p):
print repr(t[p:p + len(s)].lower())
if t[p:p + len(s)].lower() == s:
return t[p:p + len(s)]
return None
def kw(s):
if keyword_normalisation == "uppercase":
return s.upper()
if keyword_normalisation == "lowercase":
return s.lower()
return s
p = 0
current_statement = None
current_state = None
tl = len(text)
token = None
while p < tl:
tt, token, e = get_token(text, p)
if not token:
break
print "got token %-60.60r of type %r at %s %s" % (token, tt, current_statement, current_state)
if tt == "function call":
# uniform pretty print a function call
token = pretty_print_function_call(token)
if token.startswith("#"):
# comment line. skipping to eol
e = text.find("\n", e)
if e == -1:
# last line
break
p = e
continue
if token.startswith("/*"):
# comment line. skipping to eol
e = text.find("*/", p)
if e == -1:
break
p = e + 2
continue
if token == ";":
# new query
current_statement = None
current_state = None
output.write(token)
output.write("\n")
p = e
continue;
if token.lower() == "select":
# start of select statement!
output.write(kw(token))
output.write("\n\t")
p = e
current_statement = "select"
current_state = "fields"
continue
if token.lower() == "limit":
output.write("\n")
output.write(kw(token))
output.write("\n\t")
current_statement = "limit"
p = e
continue
if current_statement == "limit":
output.write(token)
if token == ",":
output.write(" ")
p = e
continue
if token.lower() == "order":
output.write("\n")
output.write(kw(token))
output.write(" ") # wait for by :)
p = e
current_statement = "order"
continue
if token.lower() == "by":
output.write(kw(token))
output.write("\n\t")
current_state = "order_fields"
order_dir = None
p = e
continue
if token.lower() == "where":
output.write("\n")
output.write(kw(token))
output.write("\n\t")
current_statement = "where"
p = e
continue
if current_statement == "where":
if token.lower() in ("and", "or"):
output.write("\n\t")
output.write(kw(token))
else:
output.write(token)
p = e
continue
if current_statement == "select" and current_state == "fields":
if token.lower() == "from":
output.write("\n")
output.write(kw(token))
output.write("\n\t")
p = e
current_state = "tables"
continue
output.write(token)
if token == ",":
output.write("\n\t")
p = e
continue
if current_statement == "select" and current_state == "tables":
output.write(token)
if token == ",":
output.write("\n\t")
p = e
continue
if current_statement == "order":
if token.lower() == "desc" or token.lower() == "asc":
output.write(" ")
output.write(kw(token))
else:
output.write(token)
if token == ",":
output.write("\n\t")
p = e
continue
break
while True:
break
o = strspn(text, " \r\n\t", p)
print "span : ", o
s = p + o
print "start: %r" % text[s:]
break
self.set_query_text(q, output.getvalue())
def on_compress(self, button):
q = self.emma.current_query
text = self.get_query_text(q)
print "input: %r" % text
output = cStringIO.StringIO()
keyword_normalisation = "u"
def kw(s):
if keyword_normalisation == "uppercase":
return s.upper()
if keyword_normalisation == "lowercase":
return s.lower()
return s
p = 0
tl = len(text)
token = None
keywords = "select,from,left,join,right,inner,where,and,or,order,by,having,group,limit,union,distinct"
keywords = keywords.split(",")
token = None
last_token = token
while p < tl:
tt, token, e = get_token(text, p)
if not token:
break
print "token : %r last_token: %r" % (token, last_token)
if tt == "function call":
# uniform pretty print a function call
token = pretty_print_function_call(token, compressed=True)
if token.startswith("#"):
# comment line. skipping to eol
e = text.find("\n", e)
if e == -1:
# last line
break
p = e
continue
if token.startswith("/*"):
# comment line. skipping to eol
e = text.find("*/", p)
if e == -1:
break
p = e + 2
continue
if token == ";":
# new query
current_statement = None
current_state = None
output.write(token)
output.write("\n")
last_token = token
p = e
continue;
if token not in ",=" and last_token != None and last_token not in ",=;":
output.write(" ")
if token.lower() in keywords:
output.write(kw(token))
p = e
last_token = token
continue
output.write(token)
p = e
last_token = token
self.set_query_text(q, output.getvalue())
plugin_instance = None
def plugin_init(emma_instance):
global plugin_instance
plugin_instance = pretty_format(emma_instance)
return plugin_instance
def plugin_unload():
global plugin_instance
plugin_instance.cleanup()
del plugin_instance
plugin_instance = None
gc.collect()
emma-0.6/emmalib/plugins/table_editor/ 0000755 0001750 0001750 00000000000 10526165411 016145 5 ustar flo flo emma-0.6/emmalib/plugins/table_editor/__init__.py 0000644 0001750 0001750 00000034210 10517473655 020272 0 ustar flo flo # -*- coding: utf-8 -*-
# emma
#
# Copyright (C) 2006 Florian Schmidt (flo@fastflo.de)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
import sys
import time
import gtk.glade
import gobject
import pprint
import re
pp = pprint.PrettyPrinter()
LEN = 1
UNSIGNED = 2
BINARY = 4
ZERO = 8
data_type_capabilities = {
"bigint": LEN | UNSIGNED | ZERO,
"blob": LEN,
"char": LEN | BINARY,
"char unicode": LEN,
"date": 0,
"datetime": 0,
"decimal": LEN | UNSIGNED | ZERO,
"double": LEN | UNSIGNED | ZERO,
"enum": LEN,
"float": LEN | UNSIGNED | ZERO,
"int": LEN | UNSIGNED | ZERO,
"longblob": 0,
"longtext": 0,
"mediumblob": 0,
"mediumint": LEN | UNSIGNED | ZERO,
"mediumtext": 0,
"set": LEN,
"smallint": LEN | UNSIGNED | ZERO,
"text": LEN,
"time": 0,
"year": LEN,
"timestamp": LEN,
"tinyblob": 0,
"tinyint": LEN | UNSIGNED | ZERO,
"tinytext": LEN | UNSIGNED | ZERO,
"varchar": LEN | BINARY
}
def split_strings(string):
return re.findall("(?:(?:'.*?')|[^ ])+", string)
def test(c, t, f):
if c:
return t
return f
class table_editor:
def __init__(self, emma_instance):
self.emma = emma_instance
self.popup_items = []
self.plugin_dir = os.path.dirname(os.path.abspath(__file__))
self.glade_file = os.path.join(self.plugin_dir, "table_editor.glade")
if not os.access(self.glade_file, os.R_OK):
self.glade_file = os.path.join(self.emma.glade_path, "table_editor.glade")
if not os.access(self.glade_file, os.R_OK):
raise ValueError("glade file %s not found!" % self.glade_file)
else:
print "galde file:", self.glade_file
self.xml = gtk.glade.XML(self.glade_file)
self.window = self.xml.get_widget("table_editor")
self.xml.signal_autoconnect(self)
self.treeview = self.xml.get_widget("table_columns")
self.model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)
self.treeview.set_model(self.model)
self.treeview.set_headers_clickable(False)
self.treeview.set_reorderable(True)
self.treeview.insert_column_with_attributes(-1, "name", gtk.CellRendererText(), text=0)
self.treeview.insert_column_with_attributes(-1, "type", gtk.CellRendererText(), text=1)
self.treeview.insert_column_with_attributes(-1, "comment", gtk.CellRendererText(), text=2)
self.install_popup_item("table_popup", "edit table", self.edit_table)
self.ignore_changes = True
self.treeview.connect("drag-begin", self.drag_begin)
self.treeview.connect("drag-end", self.drag_end)
def row_changed(self, model, path, iter):
if self.changed_handler:
self.model.disconnect(self.changed_handler)
self.changed_handler = None
l = list(model[path][3])
l.append("reordered")
model[path][3] = l
def drag_begin(self, *args):
self.changed_handler = self.model.connect("row_changed", self.row_changed)
def drag_end(self, *args):
if self.changed_handler:
self.model.disconnect(self.changed_handler)
self.changed_handler = None
def cleanup(self):
for item, menu in self.popup_items:
menu.remove(item)
del item
self.popup_items = []
def install_popup_item(self, popup_name, item_catpion, callback):
popup = self.emma.xml.get_widget(popup_name)
for child in popup.get_children():
if child.get_child().get_text() == item_catpion:
print "%s: warning: there already is a menu item called '%s' in '%s'" % (__name__, item_caption, popup_name)
item = gtk.MenuItem(item_catpion)
item.connect("activate", callback)
item.show()
popup.append(item)
self.popup_items.append((item, popup))
def edit_table(self, menuitem):
path, column, iter, table = self.emma.get_current_table()
self.table = table
tname = self.xml.get_widget("table_name")
if not tname:
print "error: table_name-field not found in xml", self.xml
e = tname.set_text(table.name)
e = tname.grab_focus()
e = self.xml.get_widget("table_comment").set_text(table.props[14])
self.model.clear()
for name in table.field_order:
field = table.fields[name]
comment = self.extract_comment(field[5]) # todo
self.model.append(row=(field[0], field[1], comment, list(field), field))
self.xml.get_widget("table_deletefield").set_sensitive(False)
self.xml.get_widget("table_field_properties").set_sensitive(False)
self.deleted = set()
self.window.show_all()
def on_table_addfield_clicked(self, button):
unnamed_count = 0
while True:
field_name = "unnamed_%d" % unnamed_count
for row in self.model:
if row[0] == field_name:
break
else:
break
unnamed_count += 1
row = [field_name, "int", "", "", "", ""]
iter = self.model.append(row=(row[0], row[1], "", list(row), None))
self.treeview.set_cursor(self.model.get_path(iter))
self.xml.get_widget("table_field_name").grab_focus()
def on_table_columns_row_activated(self, *args):
self.xml.get_widget("table_field_name").grab_focus()
def on_table_deletefield_clicked(self, button):
print "delete field!"
path, column = self.treeview.get_cursor()
row = self.model[path]
if row[4]:
self.deleted.add(row[3][0])
del self.model[path]
self.xml.get_widget("table_deletefield").set_sensitive(False)
self.xml.get_widget("table_field_properties").set_sensitive(False)
def parse_type(self, string):
match = re.search("\(([0-9,]+)\)$", string)
if not match:
return string, None
return string[0:match.start(0)], match.group(1)
def extract_comment(self, string):
p = string.find("COMMENT")
current = split_strings(string)
for i, c in enumerate(current):
if c.startswith("comment="):
return c[len("comment=")+1:-1]
return ""
def get_field_capabilities(self, ftype):
global data_type_capabilities
try:
return data_type_capabilities[ftype]
except:
print "unknwon type", ftype, "guessing all capabilities!"
return LEN | UNSIGNED | ZERO | BINARY
def set_type_restrictions(self, ftype, capabilities=None):
if capabilities is None:
capabilities = self.get_field_capabilities(ftype)
set = self.set_field
set("table_field_length_sensitive", capabilities & LEN)
set("table_field_unsigned_sensitive", capabilities & UNSIGNED)
set("table_field_binary_sensitive", capabilities & BINARY)
def set_field(self, xml_name, value):
if xml_name.endswith("_sensitive"):
xml_name = xml_name[:-len("_sensitive")]
widget = self.xml.get_widget(xml_name)
if type(widget) != gtk.CheckButton:
widget.set_editable(test(value, True, False))
widget.set_sensitive(test(value, True, False))
return
widget = self.xml.get_widget(xml_name)
if type(widget) == gtk.Entry:
widget.set_text(str(value))
elif type(widget) == gtk.CheckButton:
widget.set_active(value)
elif type(widget) == gtk.ComboBox:
model = widget.get_model()
for i, v in enumerate(model):
if value in v[0].split(", "):
widget.set_active(i)
break
else:
print "value", value, "not found in model", model
else:
print "unknown type", type(widget)
def set_current_field(self, field):
self.ignore_changes = True
set = self.set_field
set("table_field_name", field[0])
ftype, length = self.parse_type(field[1])
set("table_field_type", ftype)
if length is None:
set("table_field_length", "")
else:
set("table_field_length", length)
default = not field[4] is None or field[2] == "YES"
set("table_field_hasdefault", default)
set("table_field_default_sensitive", default)
if default:
set("table_field_default", test(field[4] is None, "NULL", field[4]))
else:
set("table_field_default", "")
set("table_field_notnull", field[2] != "YES")
set("table_field_comment", self.extract_comment(field[5]))
set("table_field_isautoincrement", field[5].find("auto_increment") != -1)
set("table_field_unsigned", field[5].find("unsigned") != -1)
set("table_field_binary", field[5].find("binary") != -1)
self.xml.get_widget("table_field_properties").set_sensitive(True)
self.set_type_restrictions(ftype)
self.ignore_changes = False
def on_cursor_changed(self, treeview):
path, column = treeview.get_cursor()
row = self.model[path]
self.set_current_field(row[3])
self.xml.get_widget("table_deletefield").set_sensitive(True)
def on_table_field_changed(self, widget):
if self.ignore_changes:
return
def propagate_back(field, row):
row[0] = field[0] # name
row[1] = field[1] # type
def set_extra(name, active):
current = set(split_strings(row[3][5]))
if active:
current.add(name)
else:
current.discard(name)
row[3][5] = " ".join(current)
fname = widget.name
if fname.startswith("table_"):
fname = fname[len("table_"):]
path, column = self.treeview.get_cursor()
row = self.model[path]
if fname == "field_name":
row[3][0] = widget.get_text()
elif fname == "field_type" or fname == "field_length":
widget = self.xml.get_widget("table_field_type")
ftype = widget.get_active_text().split(", ")[0]
capabilities = self.get_field_capabilities(ftype)
length = self.xml.get_widget("table_field_length").get_text()
self.set_type_restrictions(ftype, capabilities)
if capabilities & LEN and length != "":
ftype += "(%s)" % length
row[3][1] = ftype
elif fname == "field_default":
row[3][4] = widget.get_text()
elif fname == "field_hasdefault":
default = widget.get_active()
self.set_field("table_field_default_sensitive", default)
if not default:
self.set_field("table_field_default", "")
row[3][4] = test(default, self.xml.get_widget("table_field_default").get_text(), None)
elif fname == "field_comment":
current = split_strings(row[3][5])
for i, c in enumerate(current):
if c.startswith("comment="):
del current[i]
break
current.append("comment='%s'" % self.table.host.escape(widget.get_text()))
row[3][5] = " ".join(current)
elif fname == "field_notnull":
row[3][2] = test(widget.get_active(), "", "YES")
elif fname == "field_isautoincrement":
set_extra("auto_increment", widget.get_active())
elif fname == "field_unsigned":
set_extra("unsigned", widget.get_active())
elif fname == "field_binary":
set_extra("binary", widget.get_active())
else:
print "unknown change in field", fname
propagate_back(row[3], row)
def on_table_abort(self, *args):
self.window.hide()
def on_table_apply(self, *args):
button = args[0]
if len(args) > 1:
mode = args[1]
else:
mode = "close"
""" render alter table sql """
esc = self.table.host.escape
query = "alter table `%s` " % esc(self.table.name)
no_changes = query
add = ""
""" deleted columns """
for f in self.deleted:
query += "%sdrop column `%s`" % (add, esc(f))
add = ",";
""" modified or new columns """
last_field = None
for f in self.model:
if tuple(f[3]) == f[4]: # no changes on this field
last_field = f[0];
continue;
#print "this field changed from\n%s to\n%s" % (f[4], f[3])
if not f[4] is None: # modified existing field
if f[4][0] != f[3][0]: # name changed
query += "%schange column `%s` `%s` %s " % (
add,
esc(f[4][0]),
esc(f[3][0]),
f[3][1]
)
else:
query += "%smodify column `%s` %s " % (
add,
esc(f[3][0]),
f[3][1]
)
else: # new field
query += "%sadd column `%s` %s " % (
add,
esc(f[3][0]),
f[3][1]
)
if f[3][2] == "YES": # NULL allowed?
query += "null "
else:
query += "not null "
if not f[3][4] is None: # default value?
query += "default '%s' " % esc(f[3][4])
if f[3][5]: # extra?
query += f[3][5] + " ";
if last_field is None:
query += "FIRST ";
else:
query += "AFTER `%s` " % esc(last_field)
add = ","
last_field = f[3][0]
# rename table
table_changed = False
new_name = self.xml.get_widget("table_name").get_text()
if new_name != self.table.name:
old_name = self.table.name
query += "%srename to `%s` " % (add, new_name)
add = ","
table_changed = True
# table comment
# todo
comment = self.xml.get_widget("table_comment").get_text()
if comment != self.table["Comment"]:
query += "%scomment = '%s' " % (add, esc(comment))
add = ""
table_changed = True
""" ask user """
if query != no_changes:
if not self.emma.confirm(
"edit table",
"do you really want to edit the %s table in database %s on %s with this sql:\n%s" % (
self.table.name,
self.table.db.name,
self.table.db.host.name,
query
),
window=self.window):
return
else:
self.window.hide()
return
""" execute sql """
print "executing"
if self.table.db.query(query): # success
""" close dialog """
self.table.create_table = None
self.table.refresh()
new_tables = self.table.db.refresh()
self.emma.redraw_db(self.table.db, self.emma.get_db_iter(self.table.db), new_tables)
self.emma.redraw_tables()
self.window.hide()
return
self.emma.show_message("edit table", "sorry, can't change table - sql error")
"""
table_editor->hide();
if(old_name != new_name) {
TreePath p = TreePath(table_iter);
p.up();
table_iter = connections_model->get_iter(p);
mysql_database_entry* db = ((mysql_table_entry*)edit_table->db)->db;
db->refresh();
update_database(db, table_iter);
} else {
((mysql_table_entry*)edit_table->db)->refresh();
on_connections_cursor_changed();
}
"""
def on_table_apply_and_open(self, button):
self.on_table_apply(button, "key_editor")
plugin_instance = None
def plugin_init(emma_instance):
global plugin_instance
plugin_instance = table_editor(emma_instance)
return plugin_instance
def plugin_unload():
global plugin_instance
plugin_instance.cleanup()
del plugin_instance
plugin_instance = None
gc.collect()
emma-0.6/emmalib/plugins/table_editor/table_editor.glade 0000644 0001750 0001750 00000071710 10441064622 021604 0 ustar flo flo
2
edit table
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_CENTER_ON_PARENT
True
True
False
gtk-edit
True
False
False
GDK_WINDOW_TYPE_HINT_DIALOG
GDK_GRAVITY_NORTH_WEST
True
False
True
False
2
True
2
2
False
0
0
True
name:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
0
1
fill
True
comment:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
1
2
fill
True
True
True
True
0
True
*
False
1
2
0
1
1
2
1
2
0
False
False
True
False
0
235
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_NONE
GTK_CORNER_TOP_LEFT
True
True
True
True
True
True
False
False
False
0
True
True
True
False
0
True
GTK_ORIENTATION_HORIZONTAL
GTK_TOOLBAR_ICONS
True
True
True
True
gtk-add
True
True
False
False
True
True
False
True
gtk-delete
True
True
False
False
True
0
True
False
2
True
False
False
0
5
True
5
2
False
0
0
True
name:
False
False
GTK_JUSTIFY_RIGHT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
0
1
fill
True
type:
False
False
GTK_JUSTIFY_RIGHT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
1
2
fill
True
True
default:
True
GTK_RELIEF_NORMAL
True
False
False
True
0
1
2
3
fill
True
comment:
False
False
GTK_JUSTIFY_RIGHT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
3
4
fill
True
length/value:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
4
5
fill
104
True
True
True
True
0
True
*
False
1
2
0
1
70
True
True
True
True
0
True
*
False
1
2
2
3
70
True
True
True
True
0
True
*
False
1
2
4
5
1
2
3
4
70
True
bigint
blob
char
char unicode
date
datetime
decimal, dec, numeric, fixed
double, real
enum
float
int, integer
longblob
longtext
mediumblob
mediumint
mediumtext
set
smallint
text
time
timestamp
tinyblob
tinyint, bool, boolean
tinytext
varchar
year
False
True
1
2
1
2
fill
fill
0
False
False
True
True
auto increment
True
GTK_RELIEF_NORMAL
True
False
False
True
0
False
False
True
True
NOT NULL
True
GTK_RELIEF_NORMAL
True
False
False
True
0
False
False
True
True
unsigned
True
GTK_RELIEF_NORMAL
True
False
False
True
0
False
False
True
True
binary
True
GTK_RELIEF_NORMAL
True
False
False
True
0
False
False
0
True
True
0
False
False
0
True
True
True
GTK_BUTTONBOX_END
10
True
True
True
gtk-cancel
True
GTK_RELIEF_NORMAL
True
True
True
True
GTK_RELIEF_NORMAL
True
True
0.5
0.5
0
0
0
0
0
0
True
False
2
True
gtk-ok
4
0.5
0.5
0
0
0
False
False
True
OK / edit keys
True
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
True
True
True
gtk-ok
True
GTK_RELIEF_NORMAL
True
5
False
False
emma-0.6/emmalib/emma.glade 0000644 0001750 0001750 00000541050 10526157200 013746 0 ustar flo flo
True
emma
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_NONE
False
1024
768
True
False
gtk-network
True
False
False
GDK_WINDOW_TYPE_HINT_NORMAL
GDK_GRAVITY_NORTH_WEST
True
False
True
True
608
True
False
0
True
False
0
True
0.5
0.5
1
1
0
0
0
0
0
True
True
True
der hat nen tooltip, verdammt!
True
GTK_RELIEF_NORMAL
True
True
gtk-dialog-info
4
0.5
0.5
0
0
2
False
False
0
False
False
True
True
224
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_NONE
GTK_CORNER_TOP_LEFT
True
True
True
True
False
False
True
False
False
False
True
False
True
True
True
False
GTK_POS_TOP
False
False
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_NONE
GTK_CORNER_TOP_LEFT
True
True
True
False
True
GTK_JUSTIFY_LEFT
GTK_WRAP_NONE
True
0
0
0
0
0
0
False
True
True
info
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
tab
True
False
0
True
GTK_ORIENTATION_HORIZONTAL
GTK_TOOLBAR_ICONS
True
True
True
True
gtk-refresh
True
True
False
False
True
True
True
True
False
True
False
0
True
auto refresh every
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
True
True
1
1
False
GTK_UPDATE_ALWAYS
False
False
0 0 100 0.5 10 10
0
True
True
True
seconds
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
False
False
True
True
True
True
False
False
True
True
True
False
True
True
server version:
False
False
GTK_JUSTIFY_LEFT
False
True
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
False
False
0
False
False
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_NONE
GTK_CORNER_TOP_LEFT
True
True
True
False
False
True
False
False
False
0
True
True
False
True
True
process list
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
tab
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_NONE
GTK_CORNER_TOP_LEFT
True
True
True
False
False
True
False
False
False
False
True
True
tables
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
tab
True
True
266
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_NONE
GTK_CORNER_TOP_LEFT
True
GTK_SHADOW_IN
True
False
0
3
True
1
2
False
0
0
0
False
True
True
2
False
True
3
True
3
3
False
0
6
0
False
True
True
False
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_NONE
GTK_CORNER_TOP_LEFT
True
True
False
False
True
GTK_JUSTIFY_LEFT
GTK_WRAP_NONE
True
0
0
0
0
0
0
True
True
False
True
True
table
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
tab
True
False
0
True
GTK_ORIENTATION_HORIZONTAL
GTK_TOOLBAR_ICONS
True
True
True
execute query (F9, Ctrl-Enter)
True
gtk-execute
True
True
False
False
True
True
True
True
False
41
True
automatic reexecution
True
0.5
1
False
GTK_UPDATE_ALWAYS
False
False
0 0 99 0.5 10 10
False
False
True
save query to file (ctrl-s)
save query
True
gtk-save
True
True
False
False
True
True
load query from file (ctrl-o)
load query
True
gtk-open
True
True
False
False
True
True
True
True
True
False
False
True
True
gtk-select-font
True
True
False
False
True
True
True
True
True
False
False
True
new query tab (ctrl-t)
True
gtk-new
True
True
False
False
True
True
close query tab (ctrl-w)
True
gtk-close
True
True
False
False
True
True
rename query tab
gtk-bold
True
True
False
False
True
True
True
True
True
False
False
0
False
False
True
True
True
False
GTK_POS_TOP
True
False
True
True
130
True
False
0
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_IN
GTK_CORNER_TOP_LEFT
True
True
True
False
True
GTK_JUSTIFY_LEFT
GTK_WRAP_NONE
True
0
0
0
0
0
0
0
True
True
True
False
True
False
0
True
GTK_ORIENTATION_VERTICAL
GTK_TOOLBAR_ICONS
True
True
True
False
add new record
gtk-add
True
True
False
False
True
True
False
delete record (ctrl+del)
gtk-delete
True
True
False
False
True
True
False
store appended row
True
gtk-apply
True
True
False
False
True
True
False
save result as csv
save result as csv
True
gtk-save-as
True
True
False
False
True
True
save result as sql insert script
True
gtk-save-as
True
True
False
False
True
True
False
search for regular expression in this result (Ctrl+f / F3)
gtk-find
True
True
False
False
True
True
set font of result view
True
gtk-select-font
True
True
False
False
True
True
False
remove order clause
True
gtk-cancel
True
True
False
False
True
True
remember order for this table!
True
gtk-dialog-warning
True
True
False
False
True
0
False
False
True
False
0
True
False
False
GTK_JUSTIFY_CENTER
False
False
0
1
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_NONE
GTK_CORNER_TOP_LEFT
True
True
True
False
False
True
False
False
False
1
True
True
True
False
0
True
True
False
True
label
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
5
1
PANGO_ELLIPSIZE_NONE
-1
True
0
0
False
False
True
click here to jump to the selected host/database
True
False
True
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
5
1
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
1
False
True
0
True
True
True
True
False
True
True
False
0
True
query1
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
True
True
False
True
gtk-close
1
0.5
0.5
0
0
0
False
False
tab
0
True
True
False
True
True
query
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
tab
True
True
0
True
True
True
False
True
True
True
False
GTK_POS_TOP
False
False
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_NONE
GTK_CORNER_TOP_LEFT
True
True
False
False
True
False
False
False
False
True
True
sql log
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
tab
True
True
GTK_POLICY_ALWAYS
GTK_POLICY_ALWAYS
GTK_SHADOW_IN
GTK_CORNER_TOP_LEFT
True
True
True
False
False
True
False
False
False
False
True
True
messages
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
tab
True
False
0
True
False
5
True
True
wrap text
True
GTK_RELIEF_NORMAL
True
False
False
False
0
False
False
True
click here to store your changes back to the database
True
update
True
GTK_RELIEF_NORMAL
True
0
False
False
True
0
False
True
True
save contents to file
True
GTK_RELIEF_NONE
True
True
gtk-save-as
4
0.5
0.5
0
0
0
False
False
True
read contents from file
True
GTK_RELIEF_NONE
True
True
gtk-open
4
0.5
0.5
0
0
0
False
False
0
False
True
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_IN
GTK_CORNER_TOP_LEFT
True
GTK_SHADOW_IN
True
True
False
False
True
GTK_JUSTIFY_LEFT
GTK_WRAP_NONE
True
0
0
0
0
0
0
0
True
True
False
True
True
blobview
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
tab
True
False
4
new connection
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_CENTER_ON_PARENT
True
False
False
gtk-new
True
False
False
GDK_WINDOW_TYPE_HINT_DIALOG
GDK_GRAVITY_NORTH_WEST
True
False
True
False
2
True
6
2
False
2
0
True
connection name:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
0
1
fill
True
hostname:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
1
2
fill
True
user:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
3
4
fill
60
True
password:
False
False
GTK_JUSTIFY_RIGHT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
4
5
fill
True
True
True
True
0
True
*
False
1
2
0
1
True
True
True
True
0
True
*
False
1
2
3
4
True
True
True
False
0
True
*
False
1
2
4
5
True
database:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
5
6
fill
True
True
True
True
0
True
*
False
1
2
5
6
True
port:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
2
3
fill
True
True
True
True
0
True
*
False
1
2
1
2
True
True
True
True
0
True
*
False
1
2
2
3
0
False
False
True
True
11
True
True
apply
True
GTK_RELIEF_NORMAL
True
0
True
True
True
True
test
True
GTK_RELIEF_NORMAL
True
0
True
True
True
True
abort
True
GTK_RELIEF_NORMAL
True
0
True
True
4
False
False
False
emma
by Florian Schmidt 2006
Extendable Mysql Managing Assistant
successor of yamysqlfront
False
Florian Schmidt <schmidt_florian at gmx.de>
translator-credits
field conditions
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_CENTER_ON_PARENT
True
False
False
True
False
False
GDK_WINDOW_TYPE_HINT_DIALOG
GDK_GRAVITY_NORTH_WEST
True
False
True
True
False
0
True
GTK_BUTTONBOX_END
True
True
True
gtk-cancel
True
GTK_RELIEF_NORMAL
True
-6
True
True
True
gtk-ok
True
GTK_RELIEF_NORMAL
True
-5
0
False
True
GTK_PACK_END
5
True
2
4
False
4
4
True
operator
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
2
3
0
1
fill
True
field
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
1
2
0
1
fill
True
logic
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
0
1
fill
True
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
1
2
fill
True
False
3
True
value
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
True
True
True
True
reset
True
GTK_RELIEF_NORMAL
True
0
False
False
3
4
0
1
fill
fill
0
False
False
local regex search
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_CENTER_ON_PARENT
True
False
False
True
False
False
GDK_WINDOW_TYPE_HINT_DIALOG
GDK_GRAVITY_NORTH_WEST
True
False
True
True
False
0
True
GTK_BUTTONBOX_END
True
True
True
gtk-cancel
True
GTK_RELIEF_NORMAL
True
-6
True
True
True
gtk-find
True
GTK_RELIEF_NORMAL
True
-5
0
False
True
GTK_PACK_END
True
False
0
True
regular expression:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
True
True
True
True
0
True
*
True
0
True
True
0
False
False
True
1
2
True
0
0
True
True
case insensitive
True
GTK_RELIEF_NORMAL
True
False
False
True
0
1
0
1
True
True
start at first row
True
GTK_RELIEF_NORMAL
True
False
False
True
1
2
0
1
0
True
True
450
300
index-/key editor
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_CENTER_ON_PARENT
True
True
False
True
False
False
GDK_WINDOW_TYPE_HINT_DIALOG
GDK_GRAVITY_NORTH_WEST
True
False
True
True
False
0
True
GTK_BUTTONBOX_END
True
True
True
gtk-cancel
True
GTK_RELIEF_NORMAL
True
-6
True
True
True
gtk-ok
True
GTK_RELIEF_NORMAL
True
-5
0
False
True
GTK_PACK_END
True
False
0
True
True
0
True
index-/key name:
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
True
True
True
False
True
True
0
True
True
2
False
False
True
GTK_BUTTONBOX_DEFAULT_STYLE
0
True
there can only be one primary key for a table
True
True
GTK_RELIEF_NORMAL
True
True
0.5
0.5
0
0
0
0
0
0
True
False
2
True
gtk-add
4
0.5
0.5
0
0
0
False
False
True
create primary key
True
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
True
create new index
True
True
GTK_RELIEF_NORMAL
True
True
0.5
0.5
0
0
0
0
0
0
True
False
2
True
gtk-add
4
0.5
0.5
0
0
0
False
False
True
create index
True
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
True
drop the selected index/key
True
True
GTK_RELIEF_NORMAL
True
True
0.5
0.5
0
0
0
0
0
0
True
False
2
True
gtk-remove
4
0.5
0.5
0
0
0
False
False
True
remove
True
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
2
False
False
True
wheter this index is a unique index
True
unique
True
GTK_RELIEF_NORMAL
True
False
False
True
2
False
True
True
True
200
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_NONE
GTK_CORNER_TOP_LEFT
True
True
True
True
False
True
False
False
False
True
True
True
False
0
True
GTK_ORIENTATION_VERTICAL
GTK_TOOLBAR_ICONS
True
True
True
add selected field to this index
True
gtk-add
True
True
False
False
True
True
remove selected field from this index
True
gtk-remove
True
True
False
False
True
0
False
False
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_AUTOMATIC
GTK_SHADOW_NONE
GTK_CORNER_TOP_LEFT
True
True
True
True
False
True
False
False
False
0
True
True
True
True
0
True
True
0
True
True
500
400
changelog
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_CENTER_ON_PARENT
False
True
False
True
False
False
GDK_WINDOW_TYPE_HINT_NORMAL
GDK_GRAVITY_NORTH_WEST
True
False
True
True
GTK_POLICY_ALWAYS
GTK_POLICY_ALWAYS
GTK_SHADOW_IN
GTK_CORNER_TOP_LEFT
True
True
False
False
True
GTK_JUSTIFY_LEFT
GTK_WRAP_NONE
False
0
0
0
0
0
0
execute query from disk
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_CENTER_ON_PARENT
True
False
False
gtk-execute
True
False
False
GDK_WINDOW_TYPE_HINT_DIALOG
GDK_GRAVITY_NORTH_WEST
True
False
4
True
6
4
False
2
2
True
GTK_BUTTONBOX_END
5
True
True
True
gtk-cancel
True
GTK_RELIEF_NORMAL
True
True
True
True
gtk-ok
True
GTK_RELIEF_NORMAL
True
0
4
5
6
fill
fill
True
False
0
True
Filename:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
True
select sql skript
GTK_FILE_CHOOSER_ACTION_OPEN
True
False
False
-1
0
True
True
0
4
0
1
fill
True
start from line:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
1
2
fill
True
True
1
0
True
GTK_UPDATE_ALWAYS
False
False
1 1 1.00000001505e+29 1 100 100
1
2
1
2
True
UI update interval in s:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
2
3
1
2
fill
True
True
1
2
True
GTK_UPDATE_ALWAYS
False
False
0.5 0 100 0.25 1 1
3
4
1
2
True
True
append to query log
True
GTK_RELIEF_NORMAL
True
False
False
True
0
2
4
5
fill
True
True
stop on error
True
GTK_RELIEF_NORMAL
True
False
False
True
2
4
4
5
fill
True
True
limit to database:
True
GTK_RELIEF_NORMAL
True
False
False
True
0
2
2
3
fill
True
False
True
True
True
0
True
*
False
2
4
2
3
True
True
exclude queries matching:
True
GTK_RELIEF_NORMAL
True
False
False
True
0
2
3
4
fill
True
False
True
True
True
0
True
*
False
2
4
3
4
308
110
execute query from disk progress
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_CENTER_ON_PARENT
True
False
False
True
False
False
GDK_WINDOW_TYPE_HINT_DIALOG
GDK_GRAVITY_NORTH_WEST
True
False
4
True
4
4
False
2
5
True
line:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
0
1
fill
True
current line number
False
True
0
True
*
False
6
1
2
0
1
True
offset:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
2
3
0
1
fill
True
current byte offset from file start
False
True
0
True
*
False
6
3
4
0
1
True
query:
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
1
1
2
fill
True
False
True
0
True
*
False
1
4
1
2
True
GTK_PROGRESS_LEFT_TO_RIGHT
0
0.10000000149
PANGO_ELLIPSIZE_NONE
0
4
2
3
fill
True
False
0
True
False
False
GTK_JUSTIFY_LEFT
False
False
0
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
True
True
True
GTK_BUTTONBOX_END
5
True
True
True
True
gtk-cancel
True
GTK_RELIEF_NORMAL
True
0
False
True
0
4
3
4
fill
emma-0.6/emmalib/__init__.py 0000644 0001750 0001750 00000342563 10526161115 014152 0 ustar flo flo # -*- coding: utf-8 -*-
# emma
#
# Copyright (C) 2006 Florian Schmidt (flo@fastflo.de)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import sys
import os
from stat import *
import time
import re
import gc
import pickle
import datetime
import bz2
try:
import gtk
from gtk import keysyms
import gobject
import gtk.gdk
import gtk.glade
if __name__ != "__main__":
from emmalib import __file__ as emmalib_file
from emmalib.mysql_host import *
from emmalib.mysql_query_tab import *
else:
emmalib_file = __file__
from mysql_host import *
from mysql_query_tab import *
except:
print "no gtk. you will not be able to start emma."
import pprint
version = "0.6"
new_instance = None
our_module = None
re_src_after_order_end = "(?:limit.*|procedure.*|for update.*|lock in share mode.*|[ \r\n\t]*$)"
re_src_after_order = "(?:[ \r\n\t]" + re_src_after_order_end + ")"
re_src_query_order = "(?is)(.*order[ \r\n\t]+by[ \r\n\t]+)(.*?)([ \r\n\t]*" + re_src_after_order_end + ")"
emmalib_file = os.path.abspath(emmalib_file)
if os.name in ["win32", "nt"]:
print "Windows detected"
emma_path = emmalib_file
count = 5
dirs_checked = []
while not os.access(os.path.join(emma_path, "emma.glade"), os.R_OK):
dirs_checked.append(emma_path)
emma_path = os.path.dirname(emma_path)
count -= 1
if not count:
print "could not find glade file! checked these dirs:", dirs_checked
sys.exit(0)
else:
emma_path = os.path.dirname(emmalib_file)
emma_share_path = os.path.join(sys.prefix, "share/emma/")
icons_path = os.path.join(emma_share_path, "icons")
glade_path = os.path.join(emma_share_path, "glade")
themes_path = os.path.join(sys.prefix, "share", "themes")
last_update = 0
class Emma:
def __init__(self):
self.created_once = {}
self.query_count = 0
self.glade_path = glade_path
self.icons_path = icons_path
self.glade_file = os.path.join(glade_path, "emma.glade")
if not os.access(self.glade_file, os.R_OK):
print self.glade_file, "not found!"
sys.exit(-1)
print "galde source file:", [self.glade_file]
self.xml = gtk.glade.XML(self.glade_file)
self.mainwindow = self.xml.get_widget("mainwindow")
self.mainwindow.connect('destroy', lambda *args: gtk.main_quit())
self.xml.signal_autoconnect(self)
self.load_icons()
# setup sql_log
self.sql_log_model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
self.sql_log_tv = self.xml.get_widget("sql_log_tv")
self.sql_log_tv.set_model(self.sql_log_model)
self.sql_log_tv.append_column(gtk.TreeViewColumn("time", gtk.CellRendererText(), text=0))
self.sql_log_tv.append_column(gtk.TreeViewColumn("query", gtk.CellRendererText(), markup=1))
if hasattr(self, "state"):
for log in self.state["sql_logs"]:
self.sql_log_model.append(log)
# setup msg
self.msg_model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
self.msg_tv = self.xml.get_widget("msg_tv")
self.msg_tv.set_model(self.msg_model)
self.msg_tv.append_column(gtk.TreeViewColumn("time", gtk.CellRendererText(), text=0))
self.msg_tv.append_column(gtk.TreeViewColumn("message", gtk.CellRendererText(), text=1))
self.blob_tv = self.xml.get_widget("blob_tv")
self.blob_tv.set_sensitive(False)
self.blob_buffer = self.blob_tv.get_buffer()
self.blob_view_visible = False
# setup connections
self.connections_model = gtk.TreeStore(gobject.TYPE_PYOBJECT);
self.connections_tv = self.xml.get_widget("connections_tv")
self.connections_tv.set_model(self.connections_model)
col = gtk.TreeViewColumn("MySQL-Hosts")
pixbuf_renderer = gtk.CellRendererPixbuf()
col.pack_start(pixbuf_renderer, False)
#col.add_attribute(pixbuf_renderer, "pixbuf", 1)
col.set_cell_data_func(pixbuf_renderer, self.render_connections_pixbuf)
text_renderer = gtk.CellRendererText()
col.pack_end(text_renderer)
#col.add_attribute(text_renderer, "text", 2)
col.set_cell_data_func(text_renderer, self.render_connections_text)
self.connections_tv.append_column(col)
self.connections_tv.connect("row-expanded", self.on_row_expanded)
self.connections_tv.connect("row-collapsed", self.on_row_collapsed)
#connections_tv.insert_column_with_data_func(-1, "MySQL-Hosts", col)
# processlist
self.processlist_tv = self.xml.get_widget("processlist_treeview")
self.processlist_model = None
self.local_search_window = self.xml.get_widget("localsearch_window")
self.local_search_entry = self.xml.get_widget("local_search_entry")
self.local_search_entry.connect("activate", lambda *a: self.local_search_window.response(gtk.RESPONSE_OK));
self.local_search_start_at_first_row = self.xml.get_widget("search_start_at_first_row")
self.local_search_case_sensitive = self.xml.get_widget("search_case_sensitive")
self.clipboard = gtk.Clipboard(gtk.gdk.display_get_default(), "CLIPBOARD")
self.pri_clipboard = gtk.Clipboard(gtk.gdk.display_get_default(), "PRIMARY")
self.field_edit = self.xml.get_widget("field_edit")
self.field_edit_content = self.xml.get_widget("edit_field_content")
self.table_property_labels = []
self.table_property_entries = []
self.table_description_size = (0, 0)
self.table_description = self.xml.get_widget("table_description")
self.query_notebook = self.xml.get_widget("query_notebook")
self.tooltips = gtk.Tooltips()
self.sort_timer_running = False
self.execution_timer_running = False
self.field_conditions_initialized = False
self.current_host = None
self.current_processlist_host = None
self.processlist_timer_running = False
self.init_config()
if not hasattr(self, "state"):
self.hosts = {}
self.load_config()
self.queries = []
self.add_query_tab(mysql_query_tab(self.xml, self.query_notebook))
else:
self.hosts = self.state["hosts"]
self.load_config(True)
self.queries = []
first = True
for q in self.state["queries"]:
if first:
xml = self.xml
else:
xml = gtk.glade.XML(self.glade_file, "first_query")
new_page = xml.get_widget("first_query")
q.__init__(xml, self.query_notebook)
self.add_query_tab(q)
if first:
first = False
self.query_notebook.set_tab_label_text(new_page, q.name)
else:
label = gtk.Label(q.name)
label.show()
self.query_notebook.append_page(new_page, label)
if self.config["theme"]:
self.select_theme(self.config["theme"])
if int(self.config["ping_connection_interval"]) > 0:
gobject.timeout_add(
int(self.config["ping_connection_interval"]) * 1000,
self.on_connection_ping
)
self.init_plugins()
def select_theme(self, theme):
theme_file = os.path.join(theme, "gtk-2.0", "gtkrc")
if not os.access(theme_file, os.R_OK):
theme_file = os.path.join(themes_path, theme, "gtk-2.0", "gtkrc")
if not os.access(theme_file, os.R_OK):
print "could not load theme file: %r" % theme_file
return
print "loading theme file %r" % theme_file
gtk.rc_parse(theme_file)
def on_reload_plugins_activate(self, *args):
self.unload_plugins()
self.load_plugins()
def init_plugin(self, plugin):
try:
plugin_init = getattr(plugin, "plugin_init")
except:
return True
plugin_init(self)
def unload_plugin(self, plugin):
try:
plugin_unload = getattr(plugin, "plugin_unload")
return plugin_unload()
except:
return True
def on_tab_close_eventbox_button_press_event(self, eventbox, event):
self.on_closequery_button_clicked(None)
def load_plugins(self):
for path in self.plugin_pathes:
for plugin_name in os.listdir(path):
plugin_dir = os.path.join(path, plugin_name)
if not os.path.isdir(plugin_dir) or plugin_name[0] == ".":
continue
if plugin_name in self.plugins:
#print "reloading plugin", plugin_name, "...",
plugin = reload(self.plugins[plugin_name])
else:
#print "loading plugin", plugin_name, "...",
plugin = __import__(plugin_name)
self.plugins[plugin_name] = plugin
ret = self.init_plugin(plugin)
#print "done", ret
def unload_plugins(self):
""" not really an unload - i just asks the module to cleanup """
for plugin_name, plugin in self.plugins.iteritems():
#print "unloading plugin", plugin_name, "...",
self.unload_plugin(plugin)
#print "done"
def init_plugins(self):
plugins_pathes = [
os.path.join(self.config_path, "plugins"),
os.path.join(emma_path, "plugins")
]
self.plugin_pathes = []
self.plugins = {}
for path in plugins_pathes:
if not os.path.isdir(path):
print "plugins-dir", path, "does not exist"
continue
if not path in sys.path:
sys.path.insert(0, path)
self.plugin_pathes.append(path)
self.load_plugins()
def __getstate__(self):
hosts = []
iter = self.connections_model.get_iter_root()
while iter:
host = self.connections_model.get_value(iter, 0)
hosts.append(host)
iter = self.connections_model.iter_next(iter)
sql_logs = []
iter = self.sql_log_model.get_iter_root()
while iter:
log = self.sql_log_model.get(iter, 0, 1, 2)
sql_logs.append(log)
iter = self.sql_log_model.iter_next(iter)
return {"hosts": hosts, "queries": self.queries, "sql_logs": sql_logs}
def init_config(self):
for i in ["HOME", "USERPROFILE"]:
filename = os.getenv(i)
if filename: break
if not filename:
filename = "."
filename = filename + "/.emma"
if os.path.isfile(filename):
print "detected emma config file", filename, "converting to directory"
temp_dir = filename + "_temp"
os.mkdir(temp_dir)
os.rename(filename, os.path.join(temp_dir, "emmarc"))
os.rename(temp_dir, filename)
self.config_path = filename
self.config_file = "emmarc"
def add_query_tab(self, qt):
self.query_count += 1
self.current_query = qt
self.queries.append(qt)
qt.set_query_encoding(self.config["db_encoding"])
qt.set_query_font(self.config["query_text_font"])
qt.set_result_font(self.config["query_result_font"])
if self.config_get_bool("query_text_wrap"):
qt.set_wrap_mode(gtk.WRAP_WORD)
else:
qt.set_wrap_mode(gtk.WRAP_NONE)
qt.set_current_host(self.current_host)
def del_query_tab(self, qt):
if self.current_query == qt:
self.current_query = None
i = self.queries.index(qt)
del self.queries[i]
def on_connection_ping(self):
iter = self.connections_model.get_iter_root()
while iter:
host = self.connections_model.get_value(iter, 0)
if host.connected:
print "pinging %s" % host.name,
if not host.ping():
print "...error! reconnect seems to fail!"
else:
print "ok"
iter = self.connections_model.iter_next(iter)
return True
def search_query_end(self, text, start):
try: r = self.query_end_re
except: r = self.query_end_re = re.compile(r'("(?:[^\\]|\\.)*?")|(\'(?:[^\\]|\\.)*?\')|(`(?:[^\\]|\\.)*?`)|(;)')
while 1:
result = re.search(r, text[start:])
if not result:
return None
start += result.end()
if result.group(4):
return start
def is_query_editable(self, query, result = None):
table, where, field, value, row_iter = self.get_unique_where(query)
if not table or not where:
return False
return True
def is_query_appendable(self, query):
if not self.current_host: return False
try: r = self.query_select_re
except: r = self.query_select_re = re.compile(r'(?i)("(?:[^\\]|\\.)*?")|(\'(?:[^\\]|\\.)*?\')|(`(?:[^\\]|\\.)*?`)|(union)|(select[ \r\n\t]+(.*)[ \r\n\t]+from[ \r\n\t]+(.*))')
start = 0
while 1:
result = re.search(r, query[start:])
if not result:
return False
start += result.end()
if result.group(4):
return False # union
if result.group(5) and result.group(6) and result.group(7):
break # found select
return result
def read_expression(self, query, start=0, concat=True, update_function=None, update_offset=0, icount=0):
# r'(?is)("(?:[^\\]|\\.)*?")|(\'(?:[^\\]|\\.)*?\')|(`(?:[^\\]|\\.)*?`)|([^ \r\n\t]*[ \r\n\t]*\()|(\))|([0-9]+(?:\\.[0-9]*)?)|([^ \r\n\t,()"\'`]+)|(,)')
try: r = self.query_expr_re
except: r = self.query_expr_re = re.compile(r"""
(?is)
("(?:[^\\]|\\.)*?")| # double quoted strings
('(?:[^\\]|\\.)*?')| # single quoted strings
(`(?:[^\\]|\\.)*?`)| # backtick quoted strings
(/\*.*?\*/)| # c-style comments
(\#.*$)| # shell-style comments
(\))| # closing parenthesis
([0-9]+(?:\\.[0-9]*)?)| # numbers
([,;])| # comma or semicolon
([^ \r\n\t\(\)]*[ \r\n\t]*\()| # opening parenthesis with leading whitespace
([^ \r\n\t,;()"'`]+) # everything else...
""", re.VERBOSE)
# print "read expr in", query
match = r.search(query, start)
#if match: print match.groups()
if not match: return (None, None)
for i in range(1, match.lastindex + 1):
if match.group(i):
t = match.group(i)
e = match.end(i)
current_token = t
if current_token[len(current_token) - 1] == "(":
while 1:
icount += 1
if update_function is not None and icount >= 10:
icount = 0
update_function(False, update_offset + e)
#print "at", [query[e:e+15]], "..."
exp, end = self.read_expression(query, e, False, update_function, update_offset, icount)
#print "got inner exp:", [exp]
if not exp: break
e = end
if concat:
t += " " + exp
if exp == ")":
break
return (t, e)
print "should not happen!"
return (None, None)
def get_order_from_query(self, query, return_before_and_after=False):
current_order = []
try: r = self.query_order_re
except: r = self.query_order_re = re.compile(re_src_query_order)
# get current order by clause
match = re.search(r, query)
if not match:
print "no order found in", [query]
print "re:", [re_src_query_order]
return current_order
before, order, after = match.groups()
order.lower()
start = 0
while 1:
item = []
while 1:
ident, end = self.read_expression(order[start:])
if not ident: break
if ident == ",": break
if ident[0] == "`":
ident = ident[1:-1]
item.append(ident)
start += end
l = len(item)
if l == 0: break
elif l == 1: item.append(True)
elif l == 2:
if item[1].lower() == "asc":
item[1] = True
else:
item[1] = False
else:
print "unknown order item:", item, "ignoring..."
item = None
if item: current_order.append(tuple(item))
if not ident: break
start += 1 # comma
return current_order
def on_remember_order_clicked(self, button):
query = self.current_query.last_source
current_order = self.get_order_from_query(query)
result = self.is_query_appendable(query)
if not result:
return (None, None, None, None, None)
table_list = result.group(7)
table_list = table_list.replace(" join ", ",")
table_list = re.sub("(?i)(?:order[ \t\r\n]by.*|limit.*|group[ \r\n\t]by.*|order[ \r\n\t]by.*|where.*)", "", table_list)
table_list = table_list.replace("`", "")
tables = map(lambda s: s.strip(), table_list.split(","))
if len(tables) > 1:
self.show_message("store table order", "can't store table order of multi-table queries!")
return
table = tables[0]
print "table: %s order: %s" % (table, current_order)
config_name = "stored_order_db_%s_table_%s" % (self.current_host.current_db.name, table)
self.config[config_name] = str(current_order)
if not self.current_host.current_db.name in self.stored_orders:
self.stored_orders[self.current_host.current_db.name] = {}
self.stored_orders[self.current_host.current_db.name][table] = current_order
self.save_config()
def get_field_list(self, s):
# todo USE IT!
fields = []
start = 0
while 1:
item = []
while 1:
ident, end = self.read_expression(s[start:])
if not ident: break
if ident == ",": break
if ident[0] == "`":
ident = ident[1:-1]
item.append(ident)
start += end
if len(item) == 1:
fields.append(item[0])
else:
fields.append(item)
if not ident: break
print "found fields:", fields
return fields
def escape_fieldname(self, field):
if not re.search("[` ]", field):
return field
return "`%s`" % field.replace("`", r"\`")
def get_unique_where(self, query, path=None, col_num=None, return_fields=False):
# call is_query_appendable before!
result = self.is_query_appendable(query)
if not result:
return (None, None, None, None, None)
field_list = result.group(6)
table_list = result.group(7)
# check tables
table_list = table_list.replace(" join ", ",")
table_list = re.sub("(?i)(?:order[ \t\r\n]by.*|limit.*|group[ \r\n\t]by.*|order[ \r\n\t]by.*|where.*)", "", table_list)
table_list = table_list.replace("`", "")
tables = table_list.split(",")
if len(tables) > 1:
print "sorry, i can't edit queries with more than one than one source-table:", tables
return (None, None, None, None, None)
# get table_name
table = tables[0].strip(" \r\n\t").strip("`'\"")
print "table:", table
# check for valid fields
field_list = re.sub("[\r\n\t ]+", " ", field_list)
field_list = re.sub("'.*?'", "__BAD__STRINGLITERAL", field_list)
field_list = re.sub("\".*?\"", "__BAD__STRINGLITERAL", field_list)
field_list = re.sub("\\(.*?\\)", "__BAD__FUNCTIONARGUMENTS", field_list)
field_list = re.sub("\\|", "__PIPE__", field_list)
temp_fields = field_list.split(",")
fields = []
for f in temp_fields:
fields.append(f.strip("` \r\n\t"))
print "fields:", fields
wildcard = False
for field in fields:
if field.find("*") != -1:
wildcard = True
break;
# find table handle!
tries = 0
new_tables = []
while 1:
try:
th = self.current_host.current_db.tables[table]
break
except:
tries += 1
if tries > 1:
print "query not editable, because table '%s' is not found in db %s" % (table, self.current_host.current_db)
return (None, None, None, None, None)
new_tables = self.current_host.current_db.refresh()
continue
# does this field really exist in this table?
c = 0
possible_primary = possible_unique = ""
unique = primary = "";
pri_okay = uni_okay = 0
for i in new_tables:
self.current_host.current_db.tables[i].refresh(False)
if not th.fields and not table in new_tables:
th.refresh(False)
row_iter = None
if path:
row_iter = self.current_query.model.get_iter(path)
# get unique where_clause
for field, field_pos in zip(th.field_order, range(len(th.field_order))):
props = th.fields[field]
if pri_okay >= 0 and props[3] == "PRI":
if possible_primary: possible_primary += ", "
possible_primary += field
if wildcard:
c = field_pos
else:
c = None
try:
c = fields.index(field)
except:
pass
if not c is None:
pri_okay = 1
if path:
value = self.current_query.model.get_value(row_iter, c)
if primary: primary += " and "
primary += "`%s`='%s'" % (field, value)
if uni_okay >= 0 and props[3] == "UNI":
if possible_unique: possible_unique += ", "
possible_unique += field
if wildcard:
c = field_pos
else:
c = None
try:
c = fields.index(field)
except:
pass
if not c is None:
uni_okay = 1
if path:
value = self.current_query.model.get_value(row_iter, c)
if unique: unique += " and "
unique += "`%s`='%s'" % (field, value)
if uni_okay < 1 and pri_okay < 1:
possible_key = "(i can't see any key-fields in this table...)"
if possible_primary:
possible_key = "e.g.'%s' would be useful!" % possible_primary;
elif possible_unique:
possible_key = "e.g.'%s' would be useful!" % possible_unique;
print "no edit-key found. try to name a key-field in your select-clause.", possible_key
return (table, None, None, None, None)
value = ""
field = None
if path:
where = primary
if not where: where = unique
if not where: where = None
if not col_num is None:
value = self.current_query.model.get_value(row_iter, col_num)
print col_num, fields
if wildcard:
field = th.field_order[col_num]
else:
field = fields[col_num]
else:
where = possible_primary + possible_unique
# get current edited field and value by col_num
#print "%s, %s, %s, %s, %s" % (table, where, field, value, row_iter)
if return_fields:
return table, where, field, value, row_iter, fields
return table, where, field, value, row_iter
def on_row_expanded(self, tv, iter, path):
o = tv.get_model().get_value(iter, 0)
if len(path) > 3: return
o.expanded = True
def on_row_collapsed(self, tv, iter, path):
o = tv.get_model().get_value(iter, 0)
if len(path) > 3: return
o.expanded = False
def on_remove_order_clicked(self, button):
query = self.current_query.last_source
try: r = self.query_order_re
except: r = self.query_order_re = re.compile(re_src_query_order)
match = re.search(r, query)
if not match:
return
before, order, after = match.groups()
new_query = re.sub("(?i)order[ \r\n\t]+by[ \r\n\t]+", "", before + after)
self.current_query.set(new_query)
self.sort_timer_running = False
self.on_execute_query_clicked()
def on_query_column_sort(self, column, col_num):
query = self.current_query.last_source
current_order = self.get_order_from_query(query)
col = column.get_title().replace("__", "_")
new_order = []
for c, o in current_order:
if c == col:
if o:
new_order.append([col, False])
col = None
else:
new_order.append([c, o])
if col:
new_order.append([self.escape_fieldname(col), True])
try: r = self.query_order_re
except: r = self.query_order_re = re.compile(re_src_query_order)
match = re.search(r, query)
if match:
before, order, after = match.groups()
order = ""
addition = ""
else:
match = re.search(re_src_after_order, query)
if not match:
before = query
after = ""
else:
before = query[0:match.start()]
after = match.group()
addition = "\norder by\n\t"
order = ""
for col, o in new_order:
if order: order += ",\n\t"
order += col
if not o: order += " desc"
if order:
new_query = ''.join([before, addition, order, after])
else:
new_query = re.sub("(?i)order[ \r\n\t]+by[ \r\n\t]+", "", before + after)
self.current_query.set(new_query)
if self.config["result_view_column_sort_timeout"] <= 0:
on_execute_query_clicked()
new_order = dict(new_order)
for col in self.current_query.treeview.get_columns():
field_name = col.get_title().replace("__", "_")
try:
sort_col = new_order[field_name]
col.set_sort_indicator(True)
if sort_col:
col.set_sort_order(gtk.SORT_ASCENDING)
else:
col.set_sort_order(gtk.SORT_DESCENDING)
except:
col.set_sort_indicator(False)
if not self.sort_timer_running:
self.sort_timer_running = True
gobject.timeout_add(
100 + int(self.config["result_view_column_sort_timeout"]),
self.on_sort_timer
)
self.sort_timer_execute = time.time() + int(self.config["result_view_column_sort_timeout"]) / 1000.
def on_sort_timer(self):
if not self.sort_timer_running:
return False # aborted
if self.sort_timer_execute > time.time():
return True # new order -> wait again
self.sort_timer_running = False
self.on_execute_query_clicked()
return False # done
def on_query_change_data(self, cellrenderer, path, new_value, col_num, force_update=False):
q = self.current_query
row_iter = q.model.get_iter(path)
if q.append_iter and q.model.iter_is_valid(q.append_iter) and q.model.get_path(q.append_iter) == q.model.get_path(row_iter):
q.filled_fields[q.treeview.get_column(col_num).get_title().replace("__", "_")] = new_value
q.model.set_value(row_iter, col_num, new_value)
return
table, where, field, value, row_iter = self.get_unique_where(q.last_source, path, col_num)
if force_update == False and new_value == value:
return
update_query = u"update `%s` set `%s`='%s' where %s limit 1" % (
table,
field,
self.current_host.escape(new_value),
where
)
if self.current_host.query(update_query, encoding=q.encoding):
print "set new value:", [new_value]
q.model.set_value(row_iter, col_num, new_value)
return True
return False
def on_blob_wrap_check_clicked(self, button):
if button.get_active():
self.blob_tv.set_wrap_mode(gtk.WRAP_WORD)
else:
self.blob_tv.set_wrap_mode(gtk.WRAP_NONE)
def on_blob_load_clicked(self, button):
d = self.assign_once("load dialog",
gtk.FileChooserDialog, "load blob contents", self.mainwindow, gtk.FILE_CHOOSER_ACTION_OPEN,
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT))
d.set_default_response(gtk.RESPONSE_ACCEPT)
answer = d.run()
d.hide()
if not answer == gtk.RESPONSE_ACCEPT: return
filename = d.get_filename()
try:
fp = file(filename, "rb")
query_text = fp.read().decode(self.current_query.encoding, "ignore")
fp.close()
except:
self.show_message("load blob contents", "loading blob contents from file %s: %s" % (filename, sys.exc_value))
return
self.blob_tv.get_buffer().set_text(query_text)
def on_blob_save_clicked(self, button):
d = self.assign_once("save dialog",
gtk.FileChooserDialog, "save blob contents", self.mainwindow, gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT))
d.set_default_response(gtk.RESPONSE_ACCEPT)
answer = d.run()
d.hide()
if not answer == gtk.RESPONSE_ACCEPT: return
filename = d.get_filename()
if os.path.exists(filename):
if not os.path.isfile(filename):
self.show_message("save blob contents", "%s already exists and is not a file!" % filename)
return
if not self.confirm("overwrite file?", "%s already exists! do you want to overwrite it?" % filename):
return
b = self.blob_tv.get_buffer()
new_value = b.get_text(b.get_start_iter(), b.get_end_iter()).encode(self.current_query.encoding, "ignore")
try:
fp = file(filename, "wb")
fp.write(new_value)
fp.close()
except:
self.show_message("save blob contents", "error writing query to file %s: %s" % (filename, sys.exc_value))
def on_delete_record_tool_clicked(self, button):
q = self.current_query
path, column = q.treeview.get_cursor()
if not path: return
row_iter = q.model.get_iter(path)
if q.append_iter and q.model.iter_is_valid(q.append_iter) and q.model.get_path(q.append_iter) == q.model.get_path(row_iter):
q.append_iter = None
q.apply_record.set_sensitive(False)
else:
table, where, field, value, row_iter = self.get_unique_where(q.last_source, path)
if not table or not where:
show_message("delete record", "could not delete this record!?")
return
update_query = "delete from `%s` where %s limit 1" % (table, where)
if not self.current_host.query(update_query, encoding=q.encoding):
return
if not q.model.remove(row_iter):
row_iter = q.model.get_iter_first()
while row_iter:
new = q.model.iter_next(row_iter)
if new is None:
break
row_iter = new
if row_iter:
q.treeview.set_cursor(q.model.get_path(row_iter))
def on_add_record_tool_clicked(self, button):
q = self.current_query
if not q.add_record.get_property("sensitive"):
return
path, column = q.treeview.get_cursor()
if path:
iter = q.model.insert_after(q.model.get_iter(path))
else:
iter = q.model.append()
q.treeview.grab_focus()
q.treeview.set_cursor(q.model.get_path(iter))
q.filled_fields = dict()
q.append_iter = iter
q.apply_record.set_sensitive(True)
def on_reload_self_activate(self, item):
pass
def on_apply_record_tool_clicked(self, button):
q = self.current_query
if not q.append_iter:
return
query = ""
for field, value in q.filled_fields.iteritems():
if query: query += ", "
if not value.isdigit():
value = "'%s'" % self.current_host.escape(value)
query += "%s=%s" % (self.escape_fieldname(field), value)
if query:
table, where, field, value, row_iter, fields = self.get_unique_where(q.last_source, return_fields=True)
update_query = "insert into `%s` set %s" % (table, query)
if not self.current_host.query(update_query, encoding=q.encoding):
return False
insert_id = self.current_host.insert_id()
print "insert id:", insert_id
where_fields = map(lambda s: s.strip(), where.split(","))
print "where fields:", where_fields
print "select fields:", fields
print "from", [table, where, field, value, row_iter]
if not where_fields:
print "no possible key found to retrieve newly created record"
else:
th = self.current_host.current_db.tables[table]
wc = []
for field in where_fields:
props = th.fields[field]
auto_increment = props[5].find("auto_increment") != -1
if auto_increment:
value = insert_id
else:
if field in q.filled_fields:
# use filled value
value = "'%s'" % self.current_host.escape(q.filled_fields[field])
else:
# use field default value (maybe none)
value = props[4]
if not value is None:
value = "'%s'" % self.current_host.escape(value)
wc.append("%s=%s" % (self.escape_fieldname(field), value))
where = " and ".join(wc)
print "select where:", where
if fields == ["*"]:
field_selector = "*"
else:
field_selector = ", ".join(map(self.escape_fieldname, fields))
self.current_host.query("select %s from `%s` where %s limit 1" % (field_selector, table, where))
result = self.current_host.handle.store_result().fetch_row(0)
if len(result) < 1:
print "error: can't find modfied row!?"
else:
row = result[0]
for index, value in enumerate(row):
if not value is None:
value = value.decode(q.encoding)
q.model.set_value(q.append_iter, index, value)
else:
q.model.remove(q.append_iter)
q.append_iter = None
q.apply_record.set_sensitive(False)
return True
def on_message_notebook_switch_page(self, nb, pointer, page):
self.blob_view_visible = (page == 2)
if self.blob_view_visible:
self.on_query_view_cursor_changed(self.current_query.treeview)
def on_query_view_cursor_changed(self, tv):
q = self.current_query
path, column = q.treeview.get_cursor()
if not path:
return
if self.blob_view_visible and column:
iter = q.model.get_iter(path)
col = q.treeview.get_columns().index(column)
self.blob_encoding = q.encoding
value = q.model.get_value(iter, col)
if value is None:
# todo signal null value
self.blob_buffer.set_text("")
else:
self.blob_buffer.set_text(value)
self.blob_tv.set_sensitive(True)
else:
self.blob_buffer.set_text("")
self.blob_tv.set_sensitive(False)
if q.append_iter:
if path == q.model.get_path(q.append_iter):
return
self.on_apply_record_tool_clicked(None)
def on_execute_query_from_disk_activate(self, button, filename=None):
if not self.current_host:
self.show_message("execute query from disk", "no host selected!")
return
d = self.get_widget("execute_query_from_disk")
fc = self.assign_once("eqfd_file_chooser", self.xml.get_widget, "eqfd_file_chooser")
if filename:
fc.set_filename(filename)
else:
#fc.set_filename("/home/flo/fact24_data_small.sql")
#fc.set_filename("/home/flo/very_small.sql")
fc.set_filename("/home/flo/out.sql")
d.show()
def on_eqfd_limit_db_toggled(self, button):
entry = self.get_widget("eqfd_db_entry")
entry.set_sensitive(button.get_active())
def on_eqfd_exclude_toggled(self, button):
entry = self.get_widget("eqfd_exclude_entry")
entry.set_sensitive(button.get_active())
def on_abort_execute_from_disk_clicked(self, button):
d = self.get_widget("execute_query_from_disk")
d.hide()
def get_widget(self, name):
return self.assign_once("widget_%s" % name, self.xml.get_widget, name)
def read_query(self, query, start=0):
try:
r = self.find_query_re
rw = self.white_find_query_re
except:
r = self.find_query_re = re.compile(r"""
(?s)
(
("(?:[^\\]|\\.)*?")| # double quoted strings
('(?:[^\\]|\\.)*?')| # single quoted strings
(`(?:[^\\]|\\.)*?`)| # backtick quoted strings
(/\*.*?\*/)| # c-style comments
(\#.*$)| # shell-style comments
([^;]) # everything but a semicolon
)+
""", re.VERBOSE)
rw = self.white_find_query_re = re.compile("[ \r\n\t]+")
m = rw.match(query, start)
if m:
start = m.end(0)
match = r.match(query, start)
if not match:
return None, len(query)
return (match.start(0), match.end(0))
def read_one_query(self, fp, start=None, count_lines=0, update_function=None, only_use_queries=False, start_line=1):
current_query = []
self.read_one_query_started = True
while self.read_one_query_started:
gc.collect()
if start is None:
while 1:
line = fp.readline()
#print "line:", [line]
if line == "":
if len(current_query) > 0:
return (' '.join(current_query), start, count_lines)
return (None, start, count_lines)
if count_lines is not None:
count_lines += 1
if update_function is not None:
lb = fp.tell() - len(line)
update_function(False, lb)
if count_lines is not None and count_lines <= start_line:
#print count_lines
continue
first = line.lstrip("\r\n\t ")[0:15].lower()
if only_use_queries and first[0:3] != "use" and first != "create database":
continue
if line.lstrip(" \t")[0:2] != "--":
break
#print "skipping line", [line]
self.last_query_line = line
start = 0
else:
lb = fp.tell() - len(self.last_query_line)
line = self.last_query_line
start, end = self.read_query(line, start)
next = line[end:end+1]
#print "next: '%s'" % next
if start is not None:
#print "append query", [line[start:end]]
current_query.append(line[start:end])
if next == ";":
return (''.join(current_query), end + 1, count_lines)
start = None
return (None, None, None)
def on_start_execute_from_disk_clicked(self, button):
host = self.current_host
d = self.get_widget("execute_query_from_disk")
fc = self.get_widget("eqfd_file_chooser")
exclude = self.get_widget("eqfd_exclude").get_active()
exclude_regex = self.get_widget("eqfd_exclude_entry").get_text()
exclude = exclude and exclude_regex
if exclude:
try:
exclude_regex = re.compile(exclude_regex, re.DOTALL)
except:
self.show_message("execute query from disk", "error compiling your regular expression: %s" % (sys.exc_value))
return
filename = fc.get_filename()
try:
sbuf = os.stat(filename)
except:
self.show_message("execute query from disk", "%s does not exists!" % filename)
return
if not S_ISREG(sbuf.st_mode):
self.show_message("execute query from disk", "%s exists, but is not a regular file!" % filename)
return
size = sbuf.st_size
try:
fp = bz2.BZ2File(filename, "r", 1024 * 8)
self.last_query_line = fp.readline()
self.using_compression = True
except:
self.using_compression = False
fp = None
if fp is None:
try:
fp = file(filename, "rb")
self.last_query_line = fp.readline()
except:
self.show_message("execute query from disk", "error opening query from file %s: %s" % (filename, sys.exc_value))
return
d.hide()
start_line = self.get_widget("eqfd_start_line").get_value()
if start_line < 1:
start_line = 1
ui = self.get_widget("eqfd_update_interval")
update_interval = ui.get_value()
if update_interval == 0:
update_interval = 2
p = self.get_widget("execute_from_disk_progress")
pb = self.get_widget("exec_progress")
offset_entry = self.get_widget("edfq_offset")
line_entry = self.get_widget("eqfd_line")
query_entry = self.get_widget("eqfd_query")
eta_label = self.get_widget("eqfd_eta")
append_to_log = self.get_widget("eqfd_append_to_log").get_active()
stop_on_error = self.get_widget("eqfd_stop_on_error").get_active()
limit_dbname = self.get_widget("eqfd_db_entry").get_text()
limit_db = self.get_widget("eqfd_limit_db").get_active() and limit_dbname != ""
if limit_db:
limit_re = re.compile("(?is)^use[ \r\n\t]+`?" + re.escape(limit_dbname) + "`?|^create database[^`]+`?" + re.escape(limit_dbname) + "`?")
limit_end_re = re.compile("(?is)^use[ \r\n\t]+`?.*`?|^create database")
last = 0
start = time.time()
def update_ui(force=False, offset=0):
global last_update
now = time.time()
if not force and now - last_update < update_interval:
return
last_update = now
pos = offset
f = float(pos) / float(size)
expired = now - start
if not self.using_compression and expired > 10:
sr = float(expired) / float(pos) * float(size - pos)
remaining = " (%.0fs remaining)" % sr
eta_label.set_text("eta: %-19.19s" % datetime.datetime.fromtimestamp(now + sr))
else:
remaining = ""
query_entry.set_text(query[0:512])
offset_entry.set_text("%d" % pos)
line_entry.set_text("%d" % current_line)
if f > 1.0:
f = 1.0
pb.set_fraction(f)
pb_text = "%.2f%%%s" % (f * 100.0, remaining)
pb.set_text(pb_text)
self.process_events()
new_line = 1
current_line = start
query = ""
p.show()
while time.time() - start < 0.10:
update_ui(True)
self.query_from_disk = True
line_offset = 0
found_db = False
while self.query_from_disk:
current_line = new_line
query, line_offset, new_line = self.read_one_query(fp, line_offset, current_line, update_ui, limit_db and not found_db, start_line)
if current_line < start_line:
current_line = start_line
if query is None:
break
if limit_db:
if not found_db:
first = query.lstrip("\r\n\t ")[0:15].lower()
if (first[0:3] == "use" or first == "create database") and limit_re.search(query):
found_db = True
else:
if limit_end_re.search(query) and not limit_re.search(query):
found_db = False
update_ui(False, fp.tell())
if not limit_db or found_db:
if exclude and exclude_regex.match(query):
print "skipping query", [query[0:80]]
elif not host.query(query, True, append_to_log) and stop_on_error:
self.show_message("execute query from disk", "an error occoured. maybe remind the line number and press cancel to close this dialog!")
self.query_from_disk = False
break
#print "exec", [query]
query = ""
update_ui(True, fp.tell())
fp.close()
if not self.query_from_disk:
self.show_message("execute query from disk", "aborted by user whish - click cancel again to close window")
return
else:
self.show_message("execute query from disk", "done!")
p.hide()
def on_cancel_execute_from_disk_clicked(self, button):
if not self.query_from_disk:
p = self.assign_once("execute_from_disk_progress", self.xml.get_widget, "execute_from_disk_progress")
p.hide()
return
self.read_one_query_started = False
self.query_from_disk = False
def on_execute_query_clicked(self, button=None, query=None):
if not self.current_query: return
q = self.current_query
if not query:
b = q.textview.get_buffer()
text = b.get_text(b.get_start_iter(), b.get_end_iter())
else:
text = query
self.current_host = host = q.current_host
if not host:
self.show_message(
"error executing this query!",
"could not execute query, because there is no selected host!"
)
return
self.current_db = q.current_db
if q.current_db:
host.select_database(q.current_db)
elif host.current_db:
if not self.confirm("query without selected db",
"""warning: this query tab has no database selected but the host-connection already has the database '%s' selected.
the author knows no way to deselect this database. do you want to continue?""" % host.current_db.name):
return
update = False
select = False
q.editable = False
# single popup
q.add_record.set_sensitive(False)
q.delete_record.set_sensitive(False)
# per query buttons
q.add_record.set_sensitive(False)
q.delete_record.set_sensitive(False)
q.apply_record.set_sensitive(False)
q.local_search.set_sensitive(False)
q.remove_order.set_sensitive(False)
q.save_result.set_sensitive(False)
q.save_result_sql.set_sensitive(False)
affected_rows = 0
last_insert_id = 0
num_rows = 0
num_fields = 0
query_time = 0
download_time = 0
display_time = 0
query_count = 0
total_start = time.time();
# cleanup last query model and treeview
for col in q.treeview.get_columns():
q.treeview.remove_column(col)
if q.model: q.model.clear()
start = 0
while start < len(text):
query_start = start
# search query end
query_start, end = self.read_query(text, start)
if query_start is None:
break;
thisquery = text[query_start:end]
start = end + 1
thisquery.strip(" \r\n\t;")
if not thisquery:
continue # empty query
query_count += 1
query_hint = re.sub("[\n\r\t ]+", " ", thisquery[:40])
q.label.set_text("executing query %d %s..." % (query_count, query_hint))
q.label.window.process_updates(False)
appendable = False
appendable_result = self.is_query_appendable(thisquery)
if appendable_result:
appendable = True
q.editable = self.is_query_editable(thisquery, appendable_result)
print "appendable: %s, editable: %s" % (appendable, q.editable)
ret = host.query(thisquery, encoding=q.encoding)
query_time += host.query_time
# if stop on error is enabled
if not ret:
print [host.last_error]
message = "error at: %s" % host.last_error.replace("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 ", "")
message = "error at: %s" % message.replace("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 ", "")
line_pos = 0
pos = message.find("at line ")
if pos != -1:
line_no = int(message[pos + 8:])
while 1:
line_no -= 1
if line_no < 1:
break
p = thisquery.find("\n", line_pos)
if p == -1:
break;
line_pos = p + 1
i = q.textview.get_buffer().get_iter_at_offset(query_start + line_pos)
match = re.search("error at: '(.*)'", message, re.DOTALL)
if match and match.group(1):
# set focus and cursor!
#print "search for ->%s<-" % match.group(1)
pos = text.find(match.group(1), query_start + line_pos, query_start + len(thisquery))
if not pos == -1:
i.set_offset(pos);
else:
match = re.match("Unknown column '(.*?')", message)
if match:
# set focus and cursor!
pos = thisquery.find(match.group(1))
if not pos == 1:
i.set_offset(query_start + pos);
q.textview.get_buffer().place_cursor(i)
q.textview.scroll_to_iter(i, 0.0)
q.textview.grab_focus()
q.label.set_text(re.sub("[\r\n\t ]+", " ", message))
return
field_count = host.handle.field_count()
if field_count == 0:
# query without result
update = True;
affected_rows += host.handle.affected_rows()
last_insert_id = host.handle.insert_id()
continue
# query with result
q.append_iter = None
q.local_search.set_sensitive(True)
q.add_record.set_sensitive(appendable)
q.delete_record.set_sensitive(q.editable)
select = True
q.last_source = thisquery
# get sort order!
sortable = True # todo
current_order = self.get_order_from_query(thisquery)
sens = False
if len(current_order) > 0: sens = True
q.remove_order.set_sensitive(sens and sortable)
sort_fields = dict()
for c, o in current_order:
sort_fields[c.lower()] = o
q.label.set_text("downloading resultset...")
q.label.window.process_updates(False)
start_download = time.time()
result = host.handle.store_result()
download_time = time.time() - start_download
if download_time < 0: download_time = 0
q.label.set_text("displaying resultset...");
q.label.window.process_updates(False)
# store field info
q.result_info = result.describe()
num_rows = result.num_rows()
for col in q.treeview.get_columns():
q.treeview.remove_column(col)
columns = [gobject.TYPE_STRING] * field_count
q.model = gtk.ListStore(*columns)
q.treeview.set_model(q.model)
q.treeview.set_rules_hint(True)
q.treeview.set_headers_clickable(True)
for i in range(field_count):
title = q.result_info[i][0].replace("_", "__").replace("[\r\n\t ]+", " ")
text_renderer = gtk.CellRendererText()
if q.editable:
text_renderer.set_property("editable", True)
text_renderer.connect("edited", self.on_query_change_data, i)
l = q.treeview.insert_column_with_data_func(-1, title, text_renderer, self.render_mysql_string, i)
col = q.treeview.get_column(l - 1)
if self.config_get_bool("result_view_column_resizable"):
col.set_resizable(True)
else:
col.set_resizable(False);
col.set_min_width(int(self.config["result_view_column_width_min"]))
col.set_max_width(int(self.config["result_view_column_width_max"]))
if sortable:
col.set_clickable(True)
col.connect("clicked", self.on_query_column_sort, i)
# set sort indicator
field_name = q.result_info[i][0].lower()
try:
sort_col = sort_fields[field_name]
col.set_sort_indicator(True)
if sort_col:
col.set_sort_order(gtk.SORT_ASCENDING)
else:
col.set_sort_order(gtk.SORT_DESCENDING)
except:
col.set_sort_indicator(False)
else:
col.set_clickable(False)
col.set_sort_indicator(False)
cnt = 0
start_display = time.time()
last_display = start_display
for row in result.fetch_row(0):
q.model.append(map(lambda f: f and f.decode(q.encoding, "replace"), row))
cnt += 1;
if not cnt % 100 == 0: continue
now = time.time()
if (now - last_display) < 0.2: continue
q.label.set_text("displayed %d rows..." % cnt)
q.label.window.process_updates(False)
last_display = now
display_time = time.time() - start_display
if display_time < 0: display_time = 0
result = []
if select:
# there was a query with a result
result.append("rows: %d" % num_rows)
result.append("fields: %d" % field_count)
q.save_result.set_sensitive(True)
q.save_result_sql.set_sensitive(True)
if update:
# there was a query without a result
result.append("affected rows: %d" % affected_rows)
result.append("insert_id: %d" % last_insert_id)
total_time = time.time() - total_start
result.append("| total time: %.2fs (query: %.2fs" % (total_time, query_time))
if select:
result.append("download: %.2fs display: %.2fs" % (download_time, display_time))
result.append(")")
q.label.set_text(' '.join(result))
self.blob_tv.set_editable(q.editable)
self.get_widget("blob_update").set_sensitive(q.editable)
self.get_widget("blob_load").set_sensitive(q.editable)
# todo update_buttons();
gc.collect()
return True
def on_save_result_clicked(self, button):
if not self.current_query:
return
d = self.assign_once("save results dialog",
gtk.FileChooserDialog, "save results", self.mainwindow, gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT))
d.set_default_response(gtk.RESPONSE_ACCEPT)
answer = d.run()
d.hide()
if not answer == gtk.RESPONSE_ACCEPT: return
filename = d.get_filename()
if os.path.exists(filename):
if not os.path.isfile(filename):
self.show_message("save results", "%s already exists and is not a file!" % filename)
return
if not self.confirm("overwrite file?", "%s already exists! do you want to overwrite it?" % filename):
return
q = self.current_query
iter = q.model.get_iter_first()
indices = range(q.model.get_n_columns())
field_delim = self.config["save_result_as_csv_delim"]
line_delim = self.config["save_result_as_csv_line_delim"]
try:
fp = file(filename, "wb")
for search, replace in {"\\n": "\n", "\\r": "\r", "\\t": "\t", "\\0": "\0"}.iteritems():
field_delim = field_delim.replace(search, replace)
line_delim = line_delim.replace(search, replace)
while iter:
row = q.model.get(iter, *indices)
for field in row:
value = field
if value is None: value = ""
fp.write(value.replace(field_delim, "\\" + field_delim))
fp.write(field_delim)
fp.write(line_delim)
iter = q.model.iter_next(iter)
fp.close()
except:
self.show_message("save results", "error writing query to file %s: %s" % (filename, sys.exc_value))
def on_save_result_sql_clicked(self, button):
if not self.current_query:
return
title = "save results as sql insert script"
d = self.assign_once("save results dialog",
gtk.FileChooserDialog, title, self.mainwindow, gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT))
d.set_default_response(gtk.RESPONSE_ACCEPT)
answer = d.run()
d.hide()
if not answer == gtk.RESPONSE_ACCEPT: return
filename = d.get_filename()
if os.path.exists(filename):
if not os.path.isfile(filename):
self.show_message(title, "%s already exists and is not a file!" % filename)
return
if not self.confirm("overwrite file?", "%s already exists! do you want to overwrite it?" % filename):
return
q = self.current_query
iter = q.model.get_iter_first()
indices = range(q.model.get_n_columns())
# try to guess target table name from query
table_name = ""
query = self.current_query.last_source
result = self.is_query_appendable(query)
if result:
table_list = result.group(7)
table_list = table_list.replace(" join ", ",")
table_list = re.sub("(?i)(?:order[ \t\r\n]by.*|limit.*|group[ \r\n\t]by.*|order[ \r\n\t]by.*|where.*)", "", table_list)
table_list = table_list.replace("`", "")
tables = map(lambda s: s.strip(), table_list.split(","))
table_name = "_".join(tables)
table_name = self.input(title, "please enter the name of the target table:", table_name)
if table_name is None:
return
table_name = self.escape_fieldname(table_name)
output_row = None
try:
fp = file(filename, "wb")
fp.write("insert into %s values" % table_name)
row_delim = "\n\t"
while iter:
row = q.model.get(iter, *indices)
if not output_row:
output_row = range(len(row))
for i, field in enumerate(row):
if field is None:
field = "NULL"
elif not field.isdigit():
field = "'%s'" % q.current_host.escape(field.encode(q.encoding))
output_row[i] = field
fp.write("%s(%s)" % (row_delim, ",".join(output_row)))
row_delim = ",\n\t"
iter = q.model.iter_next(iter)
fp.write("\n;\n")
fp.close()
except:
self.show_message(title, "error writing to file %s: %s" % (filename, sys.exc_value))
def assign_once(self, name, creator, *args):
try:
return self.created_once[name]
except:
obj = creator(*args)
self.created_once[name] = obj
return obj
def on_save_query_clicked(self, button):
if not self.current_query:
return
d = self.assign_once("save dialog",
gtk.FileChooserDialog, "save query", self.mainwindow, gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT))
d.set_default_response(gtk.RESPONSE_ACCEPT)
answer = d.run()
d.hide()
if not answer == gtk.RESPONSE_ACCEPT: return
filename = d.get_filename()
if os.path.exists(filename):
if not os.path.isfile(filename):
self.show_message("save query", "%s already exists and is not a file!" % filename)
return
if not self.confirm("overwrite file?", "%s already exists! do you want to overwrite it?" % filename):
return
b = self.current_query.textview.get_buffer()
query_text = b.get_text(b.get_start_iter(), b.get_end_iter())
try:
fp = file(filename, "wb")
fp.write(query_text)
fp.close()
except:
self.show_message("save query", "error writing query to file %s: %s" % (filename, sys.exc_value))
def on_load_query_clicked(self, button):
if not self.current_query:
return
d = self.assign_once("load dialog",
gtk.FileChooserDialog, "load query", self.mainwindow, gtk.FILE_CHOOSER_ACTION_OPEN,
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT))
d.set_default_response(gtk.RESPONSE_ACCEPT)
answer = d.run()
d.hide()
if not answer == gtk.RESPONSE_ACCEPT: return
filename = d.get_filename()
try:
sbuf = os.stat(filename)
except:
self.show_message("load query", "%s does not exists!" % filename)
return
if not S_ISREG(sbuf.st_mode):
self.show_message("load query", "%s exists, but is not a file!" % filename)
return
size = sbuf.st_size
max = int(self.config["ask_execute_query_from_disk_min_size"])
if size > max:
if self.confirm("load query", """
%s is very big (%.2fMB)!
opening it in the normal query-view may need a very long time!
if you just want to execute this skript file without editing and
syntax-highlighting, i can open this file using the execute file from disk function.
shall i do this?""" % (filename, size / 1024.0 / 1000.0)):
self.on_execute_query_from_disk_activate(None, filename)
return
try:
fp = file(filename, "rb")
query_text = fp.read()
fp.close()
except:
self.show_message("save query", "error writing query to file %s: %s" % (filename, sys.exc_value))
return
self.current_query.textview.get_buffer().set_text(query_text)
def on_save_workspace_activate(self, button):
d = self.assign_once("save workspace dialog",
gtk.FileChooserDialog, "save workspace", self.mainwindow, gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT))
d.set_default_response(gtk.RESPONSE_ACCEPT)
answer = d.run()
d.hide()
if not answer == gtk.RESPONSE_ACCEPT: return
filename = d.get_filename()
if os.path.exists(filename):
if not os.path.isfile(filename):
self.show_message("save workspace", "%s already exists and is not a file!" % filename)
return
if not self.confirm("overwrite file?", "%s already exists! do you want to overwrite it?" % filename):
return
try:
fp = file(filename, "wb")
pickle.dump(self, fp)
fp.close()
except:
self.show_message("save workspace", "error writing workspace to file %s: %s/%s" % (filename, sys.exc_type, sys.exc_value))
def on_restore_workspace_activate(self, button):
global new_instance
d = self.assign_once("restore workspace dialog",
gtk.FileChooserDialog, "restore workspace", self.mainwindow, gtk.FILE_CHOOSER_ACTION_OPEN,
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT))
d.set_default_response(gtk.RESPONSE_ACCEPT)
answer = d.run()
d.hide()
if not answer == gtk.RESPONSE_ACCEPT: return
filename = d.get_filename()
if not os.path.exists(filename):
self.show_message("restore workspace", "%s does not exists!" % filename)
return
if not os.path.isfile(filename):
self.show_message("restore workspace", "%s exists, but is not a file!" % filename)
return
try:
fp = file(filename, "rb")
print "i am unpickling:", self
new_instance = pickle.load(fp)
print "got new instance:", new_instance
fp.close()
except:
self.show_message("restore workspace", "error restoring workspace from file %s: %s/%s" % (filename, sys.exc_type, sys.exc_value))
self.mainwindow.destroy()
def __setstate__(self, state):
self.state = state
def on_local_search_button_clicked(self, button, again = False):
if not self.current_query.local_search.get_property("sensitive"):
return
query_view = self.current_query.treeview
self.local_search_start_at_first_row.set_active(False)
if not again or not self.local_search_entry.get_text():
self.local_search_entry.grab_focus()
answer = self.local_search_window.run()
self.local_search_window.hide()
if not answer == gtk.RESPONSE_OK: return
regex = self.local_search_entry.get_text()
if self.local_search_case_sensitive.get_active():
regex = "(?i)" + regex;
tm = self.current_query.model
fields = tm.get_n_columns()
start = tm.get_iter_root()
start_column_index = -1
start_path = None
if not self.local_search_start_at_first_row.get_active():
start_path, start_column = query_view.get_cursor()
if start_path:
start = tm.get_iter(start_path)
for k in range(fields):
if query_view.get_column(k) == start_column:
start_column_index = k
break
else:
start_path = None
while start:
for k in range(fields):
v = tm.get_value(start, k)
if v is None: continue
if re.search(regex, v):
path = tm.get_path(start);
if start_path and start_path == path and k <= start_column_index:
continue # skip!
column = query_view.get_column(k)
query_view.set_cursor(path, column)
query_view.scroll_to_cell(path, column)
query_view.grab_focus()
return
start = tm.iter_next(start)
self.show_message("local regex search", "sorry, no match found!\ntry to search from the beginning or execute a less restrictive query...")
def on_query_font_clicked(self, button):
d = self.assign_once("query text font", gtk.FontSelectionDialog, "select query font")
d.set_font_name(self.config["query_text_font"])
answer = d.run()
d.hide()
if not answer == gtk.RESPONSE_OK: return
font_name = d.get_font_name()
self.current_query.set_query_font(font_name)
self.config["query_text_font"] = font_name
self.save_config()
def on_query_result_font_clicked(self, button):
d = self.assign_once("query result font", gtk.FontSelectionDialog, "select result font")
d.set_font_name(self.config["query_result_font"])
answer = d.run()
d.hide()
if not answer == gtk.RESPONSE_OK: return
font_name = d.get_font_name()
self.current_query.set_result_font(font_name)
self.config["query_result_font"] = font_name
self.save_config()
def on_newquery_button_clicked(self, button):
xml = gtk.glade.XML(self.glade_file, "first_query")
tab_label_hbox = gtk.glade.XML(self.glade_file, "tab_label_hbox")
new_page = xml.get_widget("first_query")
self.add_query_tab(mysql_query_tab(xml, self.query_notebook))
label = tab_label_hbox.get_widget("tab_label_hbox")
qtlabel = tab_label_hbox.get_widget("query_tab_label")
#qtlabel.set_text("query%d" % self.query_count)
self.query_notebook.append_page(new_page, label)
self.query_notebook.set_current_page(len(self.queries) - 1)
self.current_query.textview.grab_focus()
xml.signal_autoconnect(self)
tab_label_hbox.signal_autoconnect(self)
def on_query_notebook_switch_page(self, nb, pointer, page):
if page >= len(self.queries):
page = len(self.queries) - 1
q = self.current_query = self.queries[page]
self.on_query_db_eventbox_button_press_event(None, None)
def on_closequery_button_clicked(self, button):
if len(self.queries) == 1: return
self.current_query.destroy()
self.del_query_tab(self.current_query)
self.query_notebook.remove_page(self.query_notebook.get_current_page())
gc.collect()
def on_rename_query_tab_clicked(self, button):
label = self.current_query.get_label()
new_name = self.input("rename tab", "please enter the new name of this tab:",
label.get_text()
)
if new_name is None:
return
if new_name == "":
self.current_query.last_auto_name = None
self.current_query.update_db_label()
return
self.current_query.user_rename(new_name)
def on_processlist_refresh_value_change(self, button):
value = button.get_value()
if self.processlist_timer_running: return
self.processlist_timer_running = True
self.processlist_timer_interval = value
gobject.timeout_add(int(value * 1000), self.on_processlist_refresh_timeout, button)
def on_fc_reset_clicked(self, button):
for i in range(self.fc_count):
self.fc_entry[i].set_text("")
if i == 0:
self.fc_combobox[i].set_active(0)
self.fc_op_combobox[i].set_active(0)
else:
self.fc_combobox[i].set_active(-1)
self.fc_op_combobox[i].set_active(-1)
if i: self.fc_logic_combobox[i - 1].set_active(0)
def on_quit_activate(self, item):
gtk.main_quit()
def on_about_activate(self, item):
aboutdialog = self.xml.get_widget("aboutdialog")
aboutdialog.set_version(version)
aboutdialog.run()
aboutdialog.hide()
def on_changelog_activate(self, item):
fp = file(os.path.join(emma_share_path, "changelog"))
changelog = fp.read()
fp.close()
w = self.xml.get_widget("changelog_window")
tv = self.xml.get_widget("changelog_text")
tv.get_buffer().set_text(changelog)
w.connect('delete-event', self.on_changelog_delete)
w.show()
def on_changelog_delete(self, window, event):
window.hide()
return True
def on_kill_process(self, button):
path, column = self.processlist_tv.get_cursor()
if not path or not self.current_host: return
iter = self.processlist_model.get_iter(path)
process_id = self.processlist_model.get_value(iter, 0)
if not self.current_host.query("kill %s" % process_id):
self.show_message("sorry", "there was an error while trying to kill process_id %s!" % process_id)
def on_sql_log_activate(self, *args):
if len(args) == 1:
menuitem = args[0]
if menuitem.name == "clear_all_entries":
self.sql_log_model.clear()
path, column = self.sql_log_tv.get_cursor()
row = self.sql_log_model[path]
if menuitem.name == "copy_sql_log":
self.clipboard.set_text(row[2])
self.pri_clipboard.set_text(row[2])
elif menuitem.name == "set_as_query_text":
self.current_query.textview.get_buffer().set_text(row[2])
if menuitem.name == "delete_sql_log":
iter = self.sql_log_model.get_iter(path)
self.sql_log_model.remove(iter)
return True
tv, path, tvc = args
query = tv.get_model()[path][2]
self.current_query.textview.get_buffer().set_text(query)
return True
def on_sql_log_button_press(self, tv, event):
if not event.button == 3: return False
res = tv.get_path_at_pos(int(event.x), int(event.y));
if not res: return False
self.xml.get_widget("sqllog_popup").popup(None, None, None, event.button, event.time);
return True
def on_connections_button_release(self, tv, event):
if not event.button == 3: return False
res = tv.get_path_at_pos(int(event.x), int(event.y));
menu = None
if not res or len(res[0]) == 1:
self.xml.get_widget("modify_connection").set_sensitive(not not res)
self.xml.get_widget("delete_connection").set_sensitive(not not res)
connected_host = False
if res:
model = self.connections_model
iter = model.get_iter(res[0])
host = model.get_value(iter, 0)
connected_host = host.connected
self.xml.get_widget("new_database").set_sensitive(connected_host)
self.xml.get_widget("refresh_host").set_sensitive(connected_host)
menu = self.xml.get_widget("connection_menu")
elif len(res[0]) == 2:
menu = self.xml.get_widget("database_popup")
elif len(res[0]) == 3:
menu = self.xml.get_widget("table_popup")
else: print "no popup at path depth %d\n" % res[0].size()
if menu:
menu.popup(None, None, None, event.button, event.time)
return True
def on_connections_tv_cursor_changed(self, tv):
path, column = tv.get_cursor()
nb = self.xml.get_widget("main_notebook")
if path is None:
print "get_cursor() returned none. don't know which datebase is selected."
return
if len(path) == 3 and nb.get_current_page() == 3:
print "update table view..."
self.update_table_view(path)
q = self.current_query
if not q:
return
q.last_path = path
if len(path) == 1: # host
i = self.connections_model.get_iter(path)
o = self.connections_model[i][0]
q.set_current_host(o)
elif len(path) >= 2: # database or below
i = self.connections_model.get_iter(path[0:2])
o = self.connections_model[i][0]
q.set_current_db(o)
def on_nb_change_page(self, np, pointer, page):
if page == 2:
self.redraw_tables()
return
path, column = self.connections_tv.get_cursor()
if not path: return
if len(path) == 3 and page == 3:
self.update_table_view(path)
def update_table_view(self, path = None):
if not path:
path, column = self.connections_tv.get_cursor()
if len(path) != 3: return
iter = self.connections_model.get_iter(path)
th = self.connections_model.get_value(iter, 0)
table = self.xml.get_widget("table_properties")
prop_count = len(th.props)
if len(self.table_property_labels) != prop_count:
for c in self.table_property_labels:
table.remove(c)
for c in self.table_property_entries:
table.remove(c)
self.table_property_labels = []
self.table_property_entries = []
table.resize(prop_count, 2)
r = 0
for h, p in zip(th.db.status_headers, th.props):
l = gtk.Label(h)
l.set_alignment(0, 0.5)
e = gtk.Entry()
e.set_editable(False)
if p is None: p = ""
e.set_text(p)
table.attach(l, 0, 1, r, r + 1, gtk.FILL, 0)
table.attach(e, 1, 2, r, r + 1, gtk.EXPAND|gtk.FILL|gtk.SHRINK, 0)
l.show()
e.show()
self.table_property_labels.append(l)
self.table_property_entries.append(e)
r += 1
else:
r = 0
for h, p in zip(th.db.status_headers, th.props):
l = self.table_property_labels[r]
e = self.table_property_entries[r]
l.set_label(h)
if p is None: p = ""
e.set_text(p)
r += 1
tv = self.xml.get_widget("table_textview")
tv.get_buffer().set_text(th.get_create_table())
t = self.table_description
for c in t.get_children():
self.table_description.remove(c)
self.table_description.resize(len(th.describe_headers), len(th.fields) + 1)
c = 0
for h in th.describe_headers:
l = gtk.Label(h)
t.attach(l, c, c + 1, 0, 1, gtk.FILL, 0)
l.show()
c += 1
r = 1
for fn in th.field_order:
v = th.fields[fn]
for c in range(len(th.describe_headers)):
s = v[c]
if s is None: s = ""
l = gtk.Label(s)
t.attach(l, c, c + 1, r, r + 1, gtk.FILL, 0)
l.set_alignment(0, 0.5)
l.set_selectable(True)
l.show()
r += 1
self.xml.get_widget("vbox14").check_resize()
self.tables_count = 0
self.redraw_tables()
def on_connections_row_activated(self, tv, path, col):
depth = len(path)
iter = self.connections_model.get_iter(path)
o = self.connections_model.get_value(iter, 0)
nb = self.xml.get_widget("main_notebook")
if depth == 1: # host
self.current_host = host = o
if host.connected:
self.current_host = None
host.close()
else:
host.connect()
if not host.connected: return
self.refresh_processlist()
nb.set_current_page(1)
self.redraw_host(host, iter, True)
if self.current_query:
self.current_query.set_current_host(self.current_host)
elif depth == 2: # database
self.current_host = o.host
new_tables = o.refresh()
self.redraw_db(o, iter, new_tables, True)
self.redraw_tables()
o.host.select_database(o)
if self.current_query:
self.current_query.set_current_db(o)
# self.connections_tv.expand_row(path, False)
# todo update_query_db()
elif depth == 3: # table
self.current_host = host = o.db.host
host.select_database(o.db)
table = o
if self.current_query:
self.current_query.set_current_db(table.db)
if not table.fields or (time.time() - table.last_field_read) > self.config["autorefresh_interval_table"]:
table.refresh()
self.redraw_table(o, iter)
if self.first_template:
nb.set_current_page(4)
self.on_template(None, self.first_template)
elif nb.get_current_page() < 3:
nb.set_current_page(3)
#self.connections_tv.expand_row(path, False)
# todo update_query_db();
# todo if(!doubleclick) update_table(e, i)
else:
print "No Handler for tree-depth", depth
return
def on_mainwindow_key_release_event(self, window, event):
#print "state: %d, keyval: 0x%04x, text: '%s'" % (event.state, event.keyval, event.string)
#~ RefPtr xml = queries[current_query].xml;
#~ xml_get_decl_widget_from(xml, local_search_button, Gtk::ToolButton);
#~ if(event->keyval == GDK_Tab) {
#~ return do_auto_completion();
#~ } else
#if event.keyval == keysyms.F9 or (event.state == 4 and event.keyval == keysyms.Return):
# self.on_execute_query_clicked(None)
# return True
#~ } else if(event->keyval == GDK_F6) {
#~ query_notebook->set_current_page((current_query + 1) % queries.size());
#~ return true;
#~ } else if(event->state & 4 && event->keyval == GDK_t) {
#~ new_query_tab();
#~ return true;
#~ } else if(event->state & 4 && event->keyval == GDK_w) {
#~ close_query_tab();
#~ return true;
#~ } else if(event->state & 4 && event->keyval == GDK_s) {
#~ on_save_query();
#~ return true;
#~ } else if(event->state & 4 && event->keyval == GDK_o) {
#~ on_load_query();
#~ return true;
#~ } else if(event->state & 4 && event->keyval == GDK_p) {
#~ on_pretty_format();
#~ return true;
if event.keyval == keysyms.F3:
self.on_local_search_button_clicked(None, True)
return True
#~ } else if(event->state & 4 && event->keyval == GDK_u) {
#~ xml_get_decl_widget_from(xml, query_text, Gtk::TextView);
#~ TextBuffer::iterator start, end;
#~ query_text->get_buffer()->get_selection_bounds(start, end);
#~ string text = query_text->get_buffer()->get_text(start, end);
#~ text = to_lower(text);
#~ query_text->get_buffer()->erase_selection();
#~ query_text->get_buffer()->get_selection_bounds(start, end);
#~ query_text->get_buffer()->insert(start, text);
#~ query_text->get_buffer()->get_selection_bounds(start, end);
#~ start = end;
#~ end.backward_chars(text.size());
#~ query_text->get_buffer()->select_range(start, end);
#~ return true;
#~ } else if(event->state & 4 && event->keyval == GDK_U) {
#~ xml_get_decl_widget_from(xml, query_text, Gtk::TextView);
#~ TextBuffer::iterator start, end;
#~ query_text->get_buffer()->get_selection_bounds(start, end);
#~ string text = to_upper(query_text->get_buffer()->get_text(start, end));
#~ query_text->get_buffer()->erase_selection();
#~ query_text->get_buffer()->get_selection_bounds(start, end);
#~ query_text->get_buffer()->insert(start, text);
#~ query_text->get_buffer()->get_selection_bounds(start, end);
#~ start = end;
#~ end.backward_chars(text.size());
#~ query_text->get_buffer()->select_range(start, end);
#~ return true;
#~ } else if(event->state & 4 && event->keyval >= GDK_0 && event->keyval <= GDK_9) {
#~ int page = event->keyval - GDK_0;
#~ if(page == 0) page = 10;
#~ page--;
#~ if(page < query_notebook->get_n_pages())
#~ query_notebook->set_current_page(page);
#~ // on_parse_query("vos2sql");
#~ }
#~ return false;
def on_query_view_key_press_event(self, tv, event):
q = self.current_query
path, column = q.treeview.get_cursor()
if event.keyval == keysyms.F2:
q.treeview.set_cursor(path, column, True)
return True
iter = q.model.get_iter(path)
if event.keyval == keysyms.Down and not q.model.iter_next(iter):
if q.append_iter and not self.on_apply_record_tool_clicked(None):
return True
self.on_add_record_tool_clicked(None)
return True
def on_query_view_button_release_event(self, tv, event):
if not event.button == 3: return False
res = tv.get_path_at_pos(int(event.x), int(event.y));
menu = self.xml.get_widget("result_popup")
if res:
sensitive = True
else:
sensitive = False
for c in menu.get_children():
for s in ["edit", "set ", "delete"]:
if c.name.find(s) != -1:
c.set_sensitive(sensitive and self.current_query.editable)
break
else:
if c.name not in ["add_record"]:
c.set_sensitive(sensitive)
else:
c.set_sensitive(self.current_query.add_record.get_property("sensitive"))
#menu.popup(None, None, None, event.button, event.time)
menu.popup(None, None, None, 0, event.time) # strange!
return True
def get_current_table(self):
path, column = self.connections_tv.get_cursor()
iter = self.connections_model.get_iter(path)
return path, column, iter, self.connections_model.get_value(iter, 0)
def on_table_popup(self, item):
path, column, iter, table = self.get_current_table()
what = item.name
if what == "refresh_table":
table.refresh()
self.redraw_table(table, iter)
self.update_table_view()
elif what == "truncate_table":
if not self.confirm("truncate table", "do you really want to truncate the %s table in database %s on %s?" % (table.name, table.db.name, table.db.host.name)):
return
if table.db.query("truncate `%s`" % (table.name)):
table.refresh()
self.redraw_table(table, iter)
self.update_table_view()
elif what == "drop_table":
if not self.confirm("drop table", "do you really want to DROP the %s table in database %s on %s?" % (table.name, table.db.name, table.db.host.name)):
return
db = table.db
if db.query("drop table `%s`" % (table.name)):
new_tables = db.refresh()
self.redraw_db(db, self.get_db_iter(db), new_tables)
self.redraw_tables()
def on_db_popup(self, item):
path, column = self.connections_tv.get_cursor()
iter = self.connections_model.get_iter(path)
what = item.name
db = self.connections_model.get_value(iter, 0)
if what == "refresh_database":
new_tables = db.refresh()
self.redraw_db(db, iter, new_tables)
self.redraw_tables()
elif what == "drop_database":
if not self.confirm("drop database", "do you really want to drop the %s database on %s?" % (db.name, db.host.name)):
return
host = db.host
if host.query("drop database`%s`" % (db.name)):
host.refresh()
self.redraw_host(host, self.get_host_iter(host))
elif what == "new_table":
name = self.input("new table", "please enter the name of the new table:")
if not name: return
if db.query("create table `%s` (`%s_id` int primary key auto_increment)" % (name, name)):
new_tables = db.refresh()
self.redraw_db(db, self.get_db_iter(db), new_tables)
self.redraw_tables()
def on_host_popup(self, item):
path, column = self.connections_tv.get_cursor()
if path:
iter = self.connections_model.get_iter(path)
host = self.connections_model.get_value(iter, 0)
else:
iter = None
host = None
what = item.name
if "connection_window" not in self.__dict__:
self.connection_window = self.xml.get_widget("connection_window")
self.xml.get_widget("cw_apply_button").connect("clicked", self.on_cw_apply)
self.xml.get_widget("cw_test_button").connect("clicked", self.on_cw_test)
self.xml.get_widget("cw_abort_button").connect("clicked", lambda *a: self.connection_window.hide())
self.cw_props = ["name", "host", "port", "user", "password", "database"]
if what == "refresh_host":
host.refresh()
self.redraw_host(host, iter)
elif what == "new_database":
name = self.input("new database", "please enter the name of the new database:")
if not name: return
if host.query("create database `%s`" % name):
host.refresh()
self.redraw_host(host, iter)
elif what == "modify_connection":
for n in self.cw_props:
self.xml.get_widget("cw_%s" % n).set_text(host.__dict__[n])
self.cw_mode = "edit"
self.cw_host = host
self.connection_window.show()
elif what == "delete_connection":
if not self.confirm("delete host", "do you really want to drop the host %s?" % (host.name)):
return
host.close()
self.connections_model.remove(iter)
if self.current_host == host:
self.current_host = None
del self.config["connection_%s" % host.name]
host = None
self.save_config()
elif what == "new_connection":
for n in self.cw_props:
self.xml.get_widget("cw_%s" % n).set_text("")
self.cw_mode = "new"
self.connection_window.show()
def on_cw_apply(self, *args):
if self.cw_mode == "new":
data = []
for n in self.cw_props:
data.append(self.xml.get_widget("cw_%s" % n).get_text())
if not data[0]:
self.connection_window.hide()
return
self.add_mysql_host(*data)
else:
for n in self.cw_props:
self.cw_host.__dict__[n] = self.xml.get_widget("cw_%s" % n).get_text()
self.connection_window.hide()
self.save_config()
def on_cw_test(self, *args):
import _mysql;
data = {
"connect_timeout": 6
}
widget_map = {
"password": "passwd"
}
for n in ["host", "user", "password", "port:int"]:
if ":" in n:
n, typename = n.split(":", 1)
data[widget_map.get(n, n)] = eval("%s(%r)" % (typename, self.xml.get_widget("cw_%s" % n).get_text()))
else:
data[widget_map.get(n, n)] = self.xml.get_widget("cw_%s" % n).get_text()
try:
handle = _mysql.connect(**data)
except:
self.show_message(
"test connection",
"could not connect to host %s with user %s and password %s:\n%s" % (
data["host"],
data["user"],
data["passwd"],
sys.exc_value
),
window=self.connection_window
)
return
self.show_message(
"test connection",
"successfully connected to host %s with user %s!" % (
data["host"],
data["user"]
),
window=self.connection_window)
handle.close()
def get_db_iter(self, db):
return self.get_connections_object_at_depth(db, 1)
def get_host_iter(self, host):
return self.get_connections_object_at_depth(host, 0)
def get_connections_object_at_depth(self, obj, depth):
d = 0
model = self.connections_model
iter = model.get_iter_first()
while iter:
if d == depth and model.get_value(iter, 0) == obj:
return iter
if d < depth and model.iter_has_child(iter):
iter = model.iter_children(iter)
d += 1
continue
new_iter = model.iter_next(iter)
if not new_iter:
iter = model.iter_parent(iter)
d -= 1
iter = model.iter_next(iter)
else:
iter = new_iter
return None
def on_execution_timeout(self, button):
value = button.get_value()
if value < 0.1:
self.execution_timer_running = False
return False
if self.on_execute_query_clicked() != True:
# stop on error
button.set_value(0)
value = 0
if value != self.execution_timer_interval:
self.execution_timer_running = False
self.on_reexecution_spin_changed(button)
return False
return True
def on_reexecution_spin_changed(self, button):
value = button.get_value()
if self.execution_timer_running: return
self.execution_timer_running = True
self.execution_timer_interval = value
gobject.timeout_add(int(value * 1000), self.on_execution_timeout, button)
def on_blob_update_clicked(self, button):
q = self.current_query
path, column = q.treeview.get_cursor()
iter = q.model.get_iter(path)
b = self.blob_tv.get_buffer()
new_value = b.get_text(b.get_start_iter(), b.get_end_iter())
col_max = q.model.get_n_columns()
for col_num in range(col_max):
if column == q.treeview.get_column(col_num):
break
else:
print "column not found!"
return
crs = column.get_cell_renderers()
return self.on_query_change_data(crs[0], path, new_value, col_num, force_update=self.blob_encoding != q.encoding)
def on_messages_popup(self, item):
if item.name == "clear_messages":
self.msg_model.clear()
def on_msg_tv_button_press_event(self, tv, event):
if not event.button == 3: return False
res = tv.get_path_at_pos(int(event.x), int(event.y));
self.xml.get_widget("messages_popup").popup(None, None, None, event.button, event.time);
return True
def on_query_popup(self, item):
q = self.current_query
path, column = q.treeview.get_cursor()
iter = q.model.get_iter(path)
if item.name == "copy_field_value":
col_max = q.model.get_n_columns()
for col_num in range(col_max):
if column == q.treeview.get_column(col_num):
break
else:
print "column not found!"
return
value = q.model.get_value(iter, col_num)
self.clipboard.set_text(value)
self.pri_clipboard.set_text(value)
elif item.name == "copy_record_as_csv":
col_max = q.model.get_n_columns()
value = ""
for col_num in range(col_max):
if value: value += self.config["copy_record_as_csv_delim"]
v = q.model.get_value(iter, col_num)
if not v is None: value += v
self.clipboard.set_text(value)
self.pri_clipboard.set_text(value)
elif item.name == "copy_column_as_csv":
col_max = q.model.get_n_columns()
for col_num in range(col_max):
if column == q.treeview.get_column(col_num):
break
else:
print "column not found!"
return
value = ""
iter = q.model.get_iter_first()
while iter:
if value: value += self.config["copy_record_as_csv_delim"]
v = q.model.get_value(iter, col_num)
if not v is None: value += v
iter = q.model.iter_next(iter)
self.clipboard.set_text(value)
self.pri_clipboard.set_text(value)
elif item.name == "copy_column_names":
value = ""
for col in q.treeview.get_columns():
if value: value += self.config["copy_record_as_csv_delim"]
value += col.get_title().replace("__", "_")
self.clipboard.set_text(value)
self.pri_clipboard.set_text(value)
elif item.name == "set_value_null":
col_max = q.model.get_n_columns()
for col_num in range(col_max):
if column == q.treeview.get_column(col_num):
break
else:
print "column not found!"
return
table, where, field, value, row_iter = self.get_unique_where(q.last_source, path, col_num)
update_query = "update `%s` set `%s`=NULL where %s limit 1" % (table, field, where)
if self.current_host.query(update_query, encoding=q.encoding):
q.model.set_value(row_iter, col_num, None)
elif item.name == "set_value_now":
col_max = q.model.get_n_columns()
for col_num in range(col_max):
if column == q.treeview.get_column(col_num):
break
else:
print "column not found!"
return
table, where, field, value, row_iter = self.get_unique_where(q.last_source, path, col_num)
update_query = "update `%s` set `%s`=now() where %s limit 1" % (table, field, where)
if not self.current_host.query(update_query, encoding=q.encoding):
return
self.current_host.query("select `%s` from `%s` where %s limit 1" % (field, table, where))
result = self.current_host.handle.store_result().fetch_row(0)
if len(result) < 1:
print "error: can't find modfied row!?"
return
q.model.set_value(row_iter, col_num, result[0][0])
elif item.name == "set_value_unix_timestamp":
col_max = q.model.get_n_columns()
for col_num in range(col_max):
if column == q.treeview.get_column(col_num):
break
else:
print "column not found!"
return
table, where, field, value, row_iter = self.get_unique_where(q.last_source, path, col_num)
update_query = "update `%s` set `%s`=unix_timestamp(now()) where %s limit 1" % (table, field, where)
if not self.current_host.query(update_query, encoding=q.encoding):
return
self.current_host.query("select `%s` from `%s` where %s limit 1" % (field, table, where))
result = self.current_host.handle.store_result().fetch_row(0)
if len(result) < 1:
print "error: can't find modfied row!?"
return
q.model.set_value(row_iter, col_num, result[0][0])
elif item.name == "set_value_as_password":
col_max = q.model.get_n_columns()
for col_num in range(col_max):
if column == q.treeview.get_column(col_num):
break
else:
print "column not found!"
return
table, where, field, value, row_iter = self.get_unique_where(q.last_source, path, col_num)
update_query = "update `%s` set `%s`=password('%s') where %s limit 1" % (table, field, self.current_host.escape(value), where)
if not self.current_host.query(update_query, encoding=q.encoding):
return
self.current_host.query("select `%s` from `%s` where %s limit 1" % (field, table, where))
result = self.current_host.handle.store_result().fetch_row(0)
if len(result) < 1:
print "error: can't find modfied row!?"
return
q.model.set_value(row_iter, col_num, result[0][0])
elif item.name == "set_value_to_sha":
col_max = q.model.get_n_columns()
for col_num in range(col_max):
if column == q.treeview.get_column(col_num):
break
else:
print "column not found!"
return
table, where, field, value, row_iter = self.get_unique_where(q.last_source, path, col_num)
update_query = "update `%s` set `%s`=sha1('%s') where %s limit 1" % (table, field, self.current_host.escape(value), where)
if not self.current_host.query(update_query, encoding=q.encoding):
return
self.current_host.query("select `%s` from `%s` where %s limit 1" % (field, table, where))
result = self.current_host.handle.store_result().fetch_row(0)
if len(result) < 1:
print "error: can't find modfied row!?"
return
q.model.set_value(row_iter, col_num, result[0][0])
def on_template(self, button, t):
current_table = self.get_selected_table()
current_fc_table = current_table;
if t.find("$table$") != -1:
if not current_table:
show_message("info", "no table selected!\nyou can't execute a template with $table$ in it, if you have no table selected!")
return
t = t.replace("$table$", self.escape_fieldname(current_table.name))
pos = t.find("$primary_key$")
if pos != -1:
if not current_table:
show_message("info", "no table selected!\nyou can't execute a template with $primary_key$ in it, if you have no table selected!")
return
if not current_table.fields:
show_message("info", "sorry, can't execute this template, because table '%s' has no fields!" % current_table.name)
return
# is the next token desc or asc?
result = re.search("(?i)[ \t\r\n]*(de|a)sc", t[pos:])
order_dir = ""
if result:
o = result.group(1).lower()
if o == "a":
order_dir = "asc"
else:
order_dir = "desc"
replace = ""
while 1:
primary_key = ""
for name in current_table.field_order:
props = current_table.fields[name]
if props[3] != "PRI": continue
if primary_key: primary_key += " " + order_dir + ", "
primary_key += "`%s`" % self.escape_fieldname(name)
if primary_key:
replace = primary_key
break
key = ""
for name in current_table.field_order:
props = current_table.fields[name]
if props[3] != "UNI": continue
if key: key += " " + order_dir + ", "
key += "`%s`" % self.escape_fieldname(name)
if key:
replace = key
break
replace = "`%s`" % self.escape_fieldname(current_table.field_order[0])
break
t = t.replace("$primary_key$", replace)
if t.find("$field_conditions$") != -1:
if not self.field_conditions_initialized:
self.field_conditions_initialized = True
self.fc_count = 4
self.fc_window = self.xml.get_widget("field_conditions")
table = self.xml.get_widget("fc_table")
table.resize(1 + self.fc_count, 4)
self.fc_entry = []
self.fc_combobox = []
self.fc_op_combobox = []
self.fc_logic_combobox = []
for i in range(self.fc_count):
self.fc_entry.append(gtk.Entry())
self.fc_entry[i].connect("activate", lambda *e: self.fc_window.response(gtk.RESPONSE_OK))
self.fc_combobox.append(gtk.combo_box_new_text())
self.fc_op_combobox.append(gtk.combo_box_new_text())
self.fc_op_combobox[i].append_text("=")
self.fc_op_combobox[i].append_text("<")
self.fc_op_combobox[i].append_text(">")
self.fc_op_combobox[i].append_text("!=")
self.fc_op_combobox[i].append_text("LIKE")
self.fc_op_combobox[i].append_text("NOT LIKE")
self.fc_op_combobox[i].append_text("ISNULL")
self.fc_op_combobox[i].append_text("NOT ISNULL")
if i:
self.fc_logic_combobox.append(gtk.combo_box_new_text())
self.fc_logic_combobox[i - 1].append_text("disabled")
self.fc_logic_combobox[i - 1].append_text("AND")
self.fc_logic_combobox[i - 1].append_text("OR")
table.attach(self.fc_logic_combobox[i - 1], 0, 1, i + 1, i + 2)
self.fc_logic_combobox[i - 1].show()
table.attach(self.fc_combobox[i], 1, 2, i + 1, i + 2);
table.attach(self.fc_op_combobox[i], 2, 3, i + 1, i + 2)
table.attach(self.fc_entry[i], 3, 4, i + 1, i + 2);
self.fc_combobox[i].show();
self.fc_op_combobox[i].show();
self.fc_entry[i].show();
if not current_table:
show_message("info", "no table selected!\nyou can't execute a template with $field_conditions$ in it, if you have no table selected!")
return
last_field = []
for i in range(self.fc_count):
last_field.append(self.fc_combobox[i].get_active_text())
self.fc_combobox[i].get_model().clear()
if i:
self.fc_logic_combobox[i - 1].set_active(0)
fc = 0
for field_name in current_table.field_order:
for k in range(self.fc_count):
self.fc_combobox[k].append_text(field_name)
if last_field[k] == field_name:
self.fc_combobox[k].set_active(fc)
fc += 1
if not self.fc_op_combobox[0].get_active_text(): self.fc_op_combobox[0].set_active(0)
if not self.fc_combobox[0].get_active_text(): self.fc_combobox[0].set_active(0)
answer = self.fc_window.run()
self.fc_window.hide()
if answer != gtk.RESPONSE_OK: return
def field_operator_value(field, op, value):
if op == "ISNULL":
return "isnull(`%s`)" % field
if op == "NOT ISNULL":
return "not isnull(`%s`)" % field
eval_kw = "eval: "
if value.startswith(eval_kw):
return "`%s` %s %s" % (field, op, value[len(eval_kw):])
return "`%s` %s '%s'" % (field, op, self.current_host.escape(value))
conditions = "%s" % (
field_operator_value(
self.fc_combobox[0].get_active_text(),
self.fc_op_combobox[0].get_active_text(),
self.fc_entry[0].get_text()
)
)
for i in range(1, self.fc_count):
if self.fc_logic_combobox[i - 1].get_active_text() == "disabled" or self.fc_combobox[i].get_active_text() == "" or self.fc_op_combobox[i].get_active_text() == "":
continue
conditions += " %s %s" % (
self.fc_logic_combobox[i - 1].get_active_text(),
field_operator_value(
self.fc_combobox[i].get_active_text(),
self.fc_op_combobox[i].get_active_text(),
self.fc_entry[i].get_text()
)
)
t = t.replace("$field_conditions$", conditions)
try:
new_order = self.stored_orders[self.current_host.current_db.name][current_table.name]
print "found stored order", new_order
query = t
try: r = self.query_order_re
except: r = self.query_order_re = re.compile(re_src_query_order)
match = re.search(r, query)
if match:
before, order, after = match.groups()
order = ""
addition = ""
else:
match = re.search(re_src_after_order, query)
if not match:
before = query
after = ""
else:
before = query[0:match.start()]
after = match.group()
addition = "\norder by\n\t"
order = ""
for col, o in new_order:
if order: order += ",\n\t"
order += col
if not o: order += " desc"
if order:
new_query = ''.join([before, addition, order, after])
else:
new_query = re.sub("(?i)order[ \r\n\t]+by[ \r\n\t]+", "", before + after)
t = new_query
except:
pass
self.on_execute_query_clicked(None, t)
def on_processlist_refresh_timeout(self, button):
value = button.get_value()
if value < 0.1:
self.processlist_timer_running = False
return False
self.refresh_processlist()
if value != self.processlist_timer_interval:
self.processlist_timer_running = False
self.on_processlist_refresh_value_change(button)
return False
return True
def on_processlist_button_release(self, tv, event):
if not event.button == 3: return False
res = tv.get_path_at_pos(int(event.x), int(event.y));
if not res: return False
self.xml.get_widget("processlist_popup").popup(None, None, None, event.button, event.time)
def show_message(self, title, message, window=None):
if window is None:
window = self.mainwindow
dialog = gtk.MessageDialog(window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, message)
dialog.label.set_property("use-markup", True)
dialog.set_title(title)
dialog.run()
dialog.hide()
def confirm(self, title, message, window=None):
if window is None:
window = self.mainwindow
dialog = gtk.MessageDialog(window, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, message)
dialog.label.set_property("use-markup", True)
dialog.set_title(title)
answer = dialog.run()
dialog.hide()
return answer == gtk.RESPONSE_YES
def input(self, title, message, default="", window=None):
if window is None:
window = self.mainwindow
dialog = gtk.Dialog(title, window, gtk.DIALOG_MODAL,
(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT,
gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
label = gtk.Label(message)
label.set_property("use-markup", True)
dialog.vbox.pack_start(label, True, True, 2)
entry = gtk.Entry()
entry.connect("activate", lambda *a: dialog.response(gtk.RESPONSE_ACCEPT))
dialog.vbox.pack_start(entry, False, True, 2)
label.show()
entry.show()
entry.set_text(default)
answer = dialog.run()
dialog.hide()
if answer != gtk.RESPONSE_ACCEPT:
return None
return entry.get_text()
def render_connections_pixbuf(self, column, cell, model, iter):
d = model.iter_depth(iter)
o = model.get_value(iter, 0)
if d == 0:
if o.connected:
cell.set_property("pixbuf", self.icons["host"])
else:
cell.set_property("pixbuf", self.icons["offline_host"])
elif d == 1:
cell.set_property("pixbuf", self.icons["db"])
elif d == 2:
cell.set_property("pixbuf", self.icons["table"])
elif d == 3:
cell.set_property("pixbuf", self.icons["field"])
else:
print "unknown depth", d," for render_connections_pixbuf with object", o
def on_new_file_activate(self, *args):
print "new file", args
def render_connections_text(self, column, cell, model, iter):
d = model.iter_depth(iter)
o = model.get_value(iter, 0)
if d == 0:
if o.connected:
cell.set_property("text", o.name)
else:
cell.set_property("text", "(%s)" % o.name)
elif d == 3: #fields are only strings
cell.set_property("text", "%s %s" % (o[0], o[1]))
else: # everything else has a name
cell.set_property("text", o.name)
#print "unknown depth", d," for render_connections_pixbuf with object", o
def render_mysql_string(self, column, cell, model, iter, id):
o = model.get_value(iter, id)
if not o is None:
cell.set_property("background", None)
if len(o) < 256:
cell.set_property("text", o)
cell.set_property("editable", True)
else:
cell.set_property("text", o[0:256] + "...")
cell.set_property("editable", False)
else:
cell.set_property("background", self.config["null_color"])
cell.set_property("text", "")
cell.set_property("editable", True)
def config_get_bool(self, name):
value = self.config[name].lower()
if value == "yes": return True
if value == "y": return True
if value == "1": return True
if value == "true": return True
if value == "t": return True
return False
def save_config(self):
if not os.path.exists(self.config_path):
print "try to create config path %r" % self.config_path
try:
os.mkdir(self.config_path)
except:
self.show_message("save config file", "could create config directory %r: %s" % (self.config_path, sys.exc_value))
return
filename = os.path.join(self.config_path, self.config_file)
try:
fp = file(filename, "w")
except:
self.show_message("save config file", "could not open %s for writing: %s" % (filename, sys.exc_value))
return
keys = self.config.keys()
keys.sort()
for name in keys:
if name.startswith("connection_"):
continue
value = self.config[name]
fp.write("%s=%s\n" % (name, value))
iter = self.connections_model.get_iter_root()
while iter:
host = self.connections_model.get_value(iter, 0)
fp.write("connection_%s=%s\n" % (host.name, host.get_connection_string()))
iter = self.connections_model.iter_next(iter)
fp.close()
def on_reread_config_activate(self, item):
self.load_config()
def load_config(self, unpickled=False):
filename = os.path.join(self.config_path, self.config_file)
# todo get_charset(self.config["db_codeset"]);
# printf("system charset: '%s'\n", self.config["db_codeset"].c_str());
# syntax_highlight_functions: grep -E -e "^[ \\t]+]*>[^\(<90-9]+\(" mysql_fun.html fun*.html | sed -r -e "s/^[^<]*]+>//" -e "s/\(.*$/,/" | tr "[:upper:]" "[:lower:]" | sort | uniq | xargs echo
self.config = {
"null_color": "#00eeaa",
"autorefresh_interval_table": "300",
"column_sort_use_newline": "true",
"query_text_font": "Monospace 8",
"query_text_wrap": "false",
"query_result_font": "Monospace 8",
"query_log_max_entry_length": "1024",
"result_view_column_width_min": "70",
"result_view_column_width_max": "300",
"result_view_column_resizable": "false",
"result_view_column_sort_timeout": "750",
"syntax_highlight_keywords": "lock, unlock, tables, kill, truncate table, alter table, host, database, field, comment, show table status, show index, add index, drop index, add primary key, add unique, drop primary key, show create table, values, insert into, into, select, show databases, show tables, show processlist, show tables, from, where, order by, group by, limit, left, join, right, inner, after, alter, as, asc, before, begin, case, column, change column, commit, create table, default, delete, desc, describe, distinct, drop, table, first, grant, having, insert, interval, insert into, limit, null, order, primary key, primary, auto_increment, rollback, set, start, temporary, union, unique, update, create database, use, key, type, uniqe key, on, type, not, unsigned",
"syntax_highlight_functions": "date_format, now, floor, rand, hour, if, minute, month, right, year, isnull",
"syntax_highlight_functions": "abs, acos, adddate, addtime, aes_decrypt, aes_encrypt, ascii, asin, atan, benchmark, bin, bit_length, ceil, ceiling, char, character_length, char_length, charset, coercibility, collation, compress, concat, concat_ws, connection_id, conv, convert_tz, cos, cot, crypt, curdate, current_date, current_time, current_timestamp, current_user, curtime, database, date, date_add, datediff, date_format, date_sub, day, dayname, dayofmonth, dayofweek, dayofyear, decode, default, degrees, des_decrypt, des_encrypt, elt, encode, encrypt, exp, export_set, extract, field, find_in_set, floor, format, found_rows, from_days, from_unixtime, get_format, get_lock, hex, hour, if, ifnull, inet_aton, inet_ntoa, insert, instr, is_free_lock, is_used_lock, last_day, last_insert_id, lcase, left, length, ln, load_file, localtime, localtimestamp, locate, log, lower, lpad, ltrim, makedate, make_set, maketime, master_pos_wait, microsecond, mid, minute, mod, month, monthname, mysql_insert_id, now, nullif, oct, octet_length, old_password, ord, order by rand, password, period_add, period_diff, pi, position, pow, power, quarter, quote, radians, rand, release_lock, repeat, replace, reverse, right, round, row_count, rpad, rtrim, schema, second, sec_to_time, session_user, sha, sign, sin, sleep, soundex, space, sqrt, str_to_date, subdate, substr, substring, substring_index, subtime, sysdate, system_user, tan, time, timediff, time_format, timestamp, timestampadd, timestampdiff, time_to_sec, to_days, trim, truncate, ucase, uncompress, uncompressed_length, unhex, unix_timestamp, upper, user, utc_date, utc_time, utc_timestamp, uuid, version, week, weekday, weekofyear, year, yearweek",
"syntax_highlight_datatypes": "binary, bit, blob, boolean, char, character, dec, decimal, double, float, int, integer, numeric, smallint, timestamp, varchar, datetime, text, mediumint, bigint, tinyint, date",
"syntax_highlight_operators": "not, and, or, like, \\<, \\>",
"syntax_highlight_fg_keyword": "#00007F",
"syntax_highlight_fg_function": "darkblue",
"syntax_highlight_fg_datatype": "#AA00AA",
"syntax_highlight_fg_operator": "#0000aa",
"syntax_highlight_fg_double-quoted-string": "#7F007F",
"syntax_highlight_fg_single-quoted-string": "#9F007F",
"syntax_highlight_fg_backtick-quoted-string": "#BF007F",
"syntax_highlight_fg_number": "#007F7F",
"syntax_highlight_fg_comment": "#007F00",
"syntax_highlight_fg_error": "red",
"pretty_print_uppercase_keywords": "false",
"pretty_print_uppercase_operators": "false",
"template1_last 150 records": "select * from $table$ order by $primary_key$ desc limit 150",
"template2_500 records in fs-order": "select * from $table$ limit 500",
"template3_quick filter 500": "select * from $table$ where $field_conditions$ limit 500",
"copy_record_as_csv_delim": ",",
"save_result_as_csv_delim": ",",
"save_result_as_csv_line_delim": "\\n",
"ping_connection_interval": "300",
"ask_execute_query_from_disk_min_size": "1024000",
"connect_timeout": "7",
"db_encoding": "latin1",
"theme": os.path.join(emma_share_path, "theme"),
"supported_db_encodings":
"latin1 (iso8859-1, cp819); "
"latin2 (iso8859-2); "
"iso8859_15 (iso8859-15); "
"utf8;"
"utf7;"
"utf16; "
"ascii (646);"
"cp437 (IBM437);"
"cp500 (EBCDIC-CP-BE); "
"cp850 (IBM850); "
"cp1140 (ibm1140); "
"cp1252 (windows-1252); "
"mac_latin2; mac_roman"
}
first = False
if not os.path.exists(filename):
print "no config file %r found. using defaults." % filename
self.config["connection_localhost"] = "localhost,root,,"
else:
try:
fp = file(filename, "r")
line_no = 0
for line in fp:
line_no += 1
line.lstrip(" \t\r\n")
if not line: continue
if line[0] == '#': continue
varval = line.split("=", 1)
name, value = map(lambda a: a.strip("\r\n \t"), varval)
value = varval[1].strip("\r\n \t")
self.config[name] = value
#setattr(self, "cfg_%s" % name, value)
fp.close()
except:
print "could not load config file %r: %s" % (filename, sys.exc_value)
self.config["connection_localhost"] = "localhost,root,,"
# split supported encodings in list
self.supported_db_encodings = map(lambda e: e.strip(), self.config["supported_db_encodings"].split(";"))
menu = self.xml.get_widget("query_encoding_menu")
for child in menu.get_children():
menu.remove(child)
self.codings = {}
for index, coding in enumerate(self.supported_db_encodings):
try:
c, description = coding.split(" ", 1)
except:
c = coding
description = ""
self.codings[c] = (index, description)
item = gtk.MenuItem(coding, False)
item.connect("activate", self.on_query_encoding_changed, (c, index))
menu.append(item)
item.show()
try:
coding = self.config["db_encoding"]
index = self.codings[coding][0]
except:
index = 0
coding, description = self.supported_db_encodings[index].split(" ", 1)
self.config["db_encoding"] = coding
# stored orders
self.stored_orders = {}
for name in self.config.keys():
if not name.startswith("stored_order_db_"):
continue
words = name.split("_")
db = words[3]
table = words[5]
if not db in self.stored_orders:
self.stored_orders[db] = {}
self.stored_orders[db][table] = eval(self.config[name])
self.first_template = None
keys = self.config.keys()
keys.sort()
toolbar = self.xml.get_widget("query_toolbar")
for child in toolbar.get_children():
if not child.name.startswith("template_"):
continue
toolbar.remove(child)
template_count = 0
for name in keys:
value = self.config[name]
if not unpickled:
prefix = "connection_"
if name.startswith(prefix):
v = value.split(",")
port = ""
p = v[0].rsplit(":", 1)
if len(p) == 2:
port = p[1]
v[0] = p[0]
self.add_mysql_host(name[len(prefix):], v[0], port, v[1], v[2], v[3])
prefix = "template";
if name.startswith(prefix):
value = value.replace("`$primary_key$`", "$primary_key$")
value = value.replace("`$table$`", "$table$")
value = value.replace("`$field_conditions$`", "$field_conditions$")
self.config[name] = value
if not self.first_template:
self.first_template = value
p = name.split("_", 1)
template_count += 1
button = gtk.ToolButton(gtk.STOCK_EXECUTE)
button.set_name("template_%d" % template_count)
button.set_tooltip(self.tooltips, "%s\n%s" % (p[1], value))
button.connect("clicked", self.on_template, value)
toolbar.insert(button, -1)
button.show()
if not unpickled: return
for h in self.hosts:
h.__init__(self.add_sql_log, self.add_msg_log)
iter = self.connections_model.append(None, [h])
h.set_update_ui(self.redraw_host, iter) # call before init!
self.redraw_host(h, iter)
self.current_host = h
def on_reload_theme_activate(self, *args):
gtk.rc_reparse_all()
def on_query_bottom_eventbox_button_press_event(self, ebox, event):
self.xml.get_widget("query_encoding_menu").popup(None, None, None, event.button, event.time);
def on_query_db_eventbox_button_press_event(self, ebox, event):
q = self.current_query
host = q.current_host
db = q.current_db
if q.last_path is not None:
try:
self.connections_model.get_iter(q.last_path)
self.connections_tv.set_cursor(q.last_path)
return
except:
# path was not valid
pass
i = self.connections_model.get_iter_root()
while i and self.connections_model.iter_is_valid(i):
if self.connections_model[i][0] == host:
break
i = self.connections_model.iter_next(i)
else:
print "host not found in connections list!"
q.current_host = q.current_db = None
q.update_db_label()
return
host_path = self.connections_model.get_path(i)
self.connections_tv.scroll_to_cell(host_path, column=None, use_align=True, row_align=0.0, col_align=0.0)
if db is None:
self.connections_tv.set_cursor(host_path)
return
k = self.connections_model.iter_children(i)
while k and self.connections_model.iter_is_valid(k):
if self.connections_model[k][0] == db:
break
k = self.connections_model.iter_next(k)
else:
print "database not found in connections list!"
q.current_db = None
q.update_db_label()
self.connections_tv.set_cursor(host_path)
return
path = self.connections_model.get_path(k)
#self.connections_tv.scroll_to_cell(path, column=None, use_align=True, row_align=0.125, col_align=0.0)
self.connections_tv.set_cursor(path)
return
def on_query_encoding_changed(self, menuitem, data):
self.current_query.set_query_encoding(data[0])
def add_mysql_host(self, name, hostname, port, user, password, database):
host = mysql_host(self.add_sql_log, self.add_msg_log, name, hostname, port, user, password, database, self.config["connect_timeout"])
iter = self.connections_model.append(None, [host])
host.set_update_ui(self.redraw_host, iter)
def add_sql_log(self, log):
olog = log
max_len = int(self.config["query_log_max_entry_length"])
if len(log) > max_len:
log = log[0:max_len] + "\n/* query with length of %d bytes truncated. */" % len(log);
# query = db_to_utf8(log);
# query = syntax_highlight_markup(query);
# query = rxx.replace(query, "[\r\n\t ]+", " ", Regexx::global);
if not log: return
now = time.time()
now = int((now - int(now)) * 100)
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
if now: timestamp = "%s.%02d" % (timestamp, now)
log = log.replace("<", "<")
log = log.replace(">", ">")
iter = self.sql_log_model.append((timestamp, log, olog))
self.sql_log_tv.scroll_to_cell(self.sql_log_model.get_path(iter))
#self.xml.get_widget("message_notebook").set_current_page(0)
self.process_events()
def process_events(self):
while gtk.events_pending():
gtk.main_iteration(False)
def add_msg_log(self, log):
if not log: return
log.replace(
"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",
"syntax error at "
)
now = time.time()
now = int((now - int(now)) * 100)
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
if now: timestamp = "%s.%02d" % (timestamp, now)
iter = self.msg_model.append((timestamp, log))
self.msg_tv.scroll_to_cell(self.msg_model.get_path(iter))
self.xml.get_widget("message_notebook").set_current_page(1)
def get_selected_table(self):
path, column = self.connections_tv.get_cursor()
depth = len(path)
iter = self.connections_model.get_iter(path)
if depth == 3:
return self.connections_model.get_value(iter, 0)
return None
def load_icons(self):
self.icons = {}
for icon in ["offline_host", "host", "db", "table", "field", "emma"]:
filename = os.path.join(icons_path, icon + ".png")
try:
self.icons[icon] = gtk.gdk.pixbuf_new_from_file(filename)
except:
print "could not load", filename
self.mainwindow.set_icon(self.icons["emma"])
def refresh_processlist(self, *args):
if not self.current_host: return
self.current_host.refresh_processlist()
self.redraw_processlist(self.current_host)
def redraw_processlist(self, host):
if not host.processlist: return
fields, rows = host.processlist
if self.processlist_model: self.processlist_model.clear()
if self.current_processlist_host != self.current_host:
self.current_processlist_host = self.current_host
self.xml.get_widget("version_label").set_text(" server version: %s" % self.current_host.handle.get_server_info());
for col in self.processlist_tv.get_columns():
self.processlist_tv.remove_column(col)
columns = [gobject.TYPE_STRING] * len(fields)
self.processlist_model = gtk.ListStore(*columns);
self.processlist_tv.set_model(self.processlist_model);
self.processlist_tv.set_headers_clickable(True);
id = 0
for field in fields:
title = field[0].replace("_", "__")
self.processlist_tv.insert_column_with_data_func(-1, title, gtk.CellRendererText(), self.render_mysql_string, id)
id += 1
for proc in rows:
self.processlist_model.append(proc)
return
def redraw_tables(self):
if not self.current_host:
return
db = self.current_host.current_db
if not db:
return
if not "tables_tv" in self.__dict__:
self.tables_tv = self.xml.get_widget("tables_treeview")
self.tables_model = None
self.tables_db = None
if not self.tables_db == db:
self.tables_db = db
if self.tables_model:
self.tables_model.clear()
for col in self.tables_tv.get_columns():
self.tables_tv.remove_column(col)
fields = db.status_headers
columns = [gobject.TYPE_STRING] * len(fields)
self.tables_model = gtk.ListStore(*columns);
self.tables_tv.set_model(self.tables_model);
id = 0
for field in fields:
title = field.replace("_", "__")
self.tables_tv.insert_column_with_data_func(-1, title, gtk.CellRendererText(), self.render_mysql_string, id)
id += 1
self.tables_count = 0
keys = db.tables.keys()
if self.tables_count == len(keys): return
self.tables_count = len(keys)
keys.sort()
self.tables_model.clear()
for name in keys:
table = db.tables[name]
self.tables_model.append(table.props)
def redraw_entry(self, obj, iter):
print "do redraw", obj, iter
def redraw_host(self, host, iter, expand = False):
#print "redraw host", host.name
if host.expanded: expand = True
# first remove exiting children of that node
i = self.connections_model.iter_children(iter)
while i and self.connections_model.iter_is_valid(i):
self.connections_model.remove(i)
# now add every database
keys = host.databases.keys()
keys.sort()
for name in keys:
db = host.databases[name]
i = self.connections_model.append(iter, (db,))
if expand:
self.connections_tv.expand_row(self.connections_model.get_path(iter), False)
expand = False
self.redraw_db(db, i)
def redraw_db(self, db, iter, new_tables = None, force_expand = False):
#print "redraw db", db.name
if not iter:
print "Error: invalid db-iterator:", iter
return
path = self.connections_model.get_path(iter)
if db.expanded:
force_expand = True
i = self.connections_model.iter_children(iter)
while i and self.connections_model.iter_is_valid(i):
self.connections_model.remove(i)
keys = db.tables.keys()
keys.sort()
iterators = {}
for name in keys:
table = db.tables[name]
i = self.connections_model.append(iter, (table,))
if force_expand:
self.connections_tv.expand_row(path, False)
force_expand = False
self.redraw_table(table, i)
iterators[name] = i
if not new_tables: return
for name in new_tables:
table = db.tables[name]
table.refresh(False)
self.redraw_table(table, iterators[name])
self.process_events()
def redraw_table(self, table, iter):
#print "redraw table", table.name
if table.expanded: self.connections_tv.expand_row(self.connections_model.get_path(iter), False)
i = self.connections_model.iter_children(iter)
while i and self.connections_model.iter_is_valid(i):
self.connections_model.remove(i)
for field in table.field_order:
i = self.connections_model.append(iter, (table.fields[field],))
class output_handler:
def __init__(self, print_stdout=False, log_file=None, log_flush=False):
self.stdout = sys.stdout
self.print_stdout = print_stdout
self.log_flush = log_flush
sys.stdout = self
if log_file:
self.log_fp = file(log_file, "a+")
else:
self.log_fp = None
self.debug = print_stdout or log_file
def write(self, s):
if self.print_stdout:
self.stdout.write(s)
if self.log_flush: self.stdout.flush()
if self.log_fp:
s = s.strip("\r\n")
if not s: # do not write empty lines to logfile
return
timestamp = str(datetime.datetime.now())[0:22]
self.log_fp.write("%s %s\n" % (timestamp, s.replace("\n", "\n " + (" " * len(timestamp)))))
if self.log_flush: self.log_fp.flush()
def usage():
print """usage: emma [-h|--help] [-d|--debug] [-l output_log [-f|--flush]]
-h|--help show this help message
-d|--debug output debug information on stdout
-l|--log FILE append all output to a specified log file
-f|--flush flush {stdout,log} after each write
"""
sys.exit(0)
def start(args):
global new_instance
debug_output = False
log_file = None
log_flush = False
skip = False
for i, arg in enumerate(args):
if skip:
skip = False
continue
if arg == "-h" or arg == "--help":
usage()
elif arg == "-d" or arg == "--debug":
debug_output = True
elif arg == "-f" or arg == "--flush":
log_flush = True
elif arg == "-l" or arg == "--log":
if i + 1 == len(args): usage()
log_file = args[i + 1]
skip = True
else:
usage()
# this singleton will be accessible as sys.stdout!
output_handler(debug_output, log_file, log_flush)
e = Emma()
while 1:
gtk.main()
del e
if not new_instance: break
e = new_instance
new_instance = None
e.__init__()
return 0
if __name__ == "__main__":
sys.exit(start(sys.argv[1:]))
emma-0.6/emmalib/mysql_db.py 0000644 0001750 0001750 00000004262 10516735662 014231 0 ustar flo flo # -*- coding: utf-8 -*-
# emma
#
# Copyright (C) 2006 Florian Schmidt (flo@fastflo.de)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import sys
import traceback
from mysql_table import *
class mysql_db:
def __init__(self, host, name = None):
self.handle = host.handle
self.host = host
if name != None:
self.name = name
self.expanded = False
self.status_headers = []
self.tables = {}
else:
print "unpickling tables!", self.handle
for name, table in self.tables.iteritems():
table.handle = self.handle
#self.id = id
def __getstate__(self):
d = dict(self.__dict__)
for i in ["handle"]:
del d[i]
#print "db will pickle:", d
return d
def refresh(self):
self.host.select_database(self)
if not self.host.query("show table status"): return
new_tables = []
result = self.handle.store_result()
self.status_headers = []
for h in result.describe():
self.status_headers.append(h[0])
old = dict(zip(self.tables.keys(), range(len(self.tables))))
for row in result.fetch_row(0):
if not row[0] in old:
#print "new table", row[0]
self.tables[row[0]] = mysql_table(self, row, result.describe())
new_tables.append(row[0])
else:
#print "known table", row[0]
# todo update self.tables[row[0]] with row!
del old[row[0]]
for table in old:
print "destroy table", table
del self.tables[table]
return new_tables
def query(self, query, check_use=True, append_to_log=True):
self.host.select_database(self)
return self.host.query(query, check_use, append_to_log)
emma-0.6/emmalib/mysql_host.py 0000644 0001750 0001750 00000012720 10516746305 014613 0 ustar flo flo # -*- coding: utf-8 -*-
# emma
#
# Copyright (C) 2006 Florian Schmidt (flo@fastflo.de)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import sys
import _mysql
import _mysql_exceptions
import time
import re
import traceback
from mysql_db import *
class mysql_host:
def __init__(self, *args):
if len(args) == 2:
# unpickle
self.sql_log, self.msg_log = args
print "unpickle host!"
if self.connected:
db_name = self.current_db.name
self.current_db = None
print "try to reconnect after unpickling!"
self.connect()
print "resulting handle:", self.handle
if self.connected:
print "unpickling databases!", self.handle
for name, db in self.databases.iteritems():
db.__init__(self)
self._use_db(db_name, True)
else:
self.sql_log, self.msg_log, self.name, self.host, self.port, self.user, self.password, self.database, self.connect_timeout = args
self.connected = False
self.databases = {} # name -> db_object
self.current_db = None
self.expanded = False
self.handle = None
self.processlist = None
self.update_ui = None
self.last_error = ""
def __getstate__(self):
d = dict(self.__dict__)
for i in ["sql_log", "msg_log", "handle", "processlist", "update_ui", "update_ui_args"]:
del d[i]
#print "host will pickle:", d
return d
def get_connection_string(self):
if self.port != "":
output = "%s:%s" % (self.host, self.port)
else:
output = "%s" % self.host
output += ",%s,%s,%s" % (self.user, self.password, self.database)
return output
def set_update_ui(self, update_ui, *args):
self.update_ui = update_ui
self.update_ui_args = args
def connect(self):
c = {
"host": self.host,
"user": self.user,
"passwd": self.password,
"connect_timeout": int(self.connect_timeout)
}
if self.port:
c["port"] = int(self.port)
if self.database:
c["db"] = self.database
try:
self.handle = _mysql.connect(**c)
except _mysql_exceptions.OperationalError:
self.connected = False
self.msg_log("%s: %s" % (sys.exc_type, sys.exc_value[1]))
return
self.connected = True
self.refresh()
if self.database: self._use_db(self.database)
def ping(self):
try:
self.handle.ping()
return True
except:
self.connected = False
self.msg_log(sys.exc_value[1])
return False
def close(self):
self.databases = {}
self.processlist = None
if self.handle:
self.handle.close()
self.handle = None
self.current_db = None
self.connected = False
if self.update_ui: self.update_ui(self, *self.update_ui_args)
def query(self, query, check_use=True, append_to_log=True, encoding=None):
if not self.handle:
self.msg_log("not connected! can't execute %s, %s, %s" % (query, str(self.handle), str(self)))
return
if append_to_log:
self.sql_log(query)
try:
self.query_time = 0
start = time.time()
if encoding:
query = query.encode(encoding, "ignore")
self.handle.query(query)
self.query_time = time.time() - start
except:
#print "error code:", sys.exc_value[0]
try:
self.last_error = sys.exc_value[1]
except:
self.last_error = str(sys.exc_value)
s = sys.exc_value[1]
#print "error:", [s]
s = s.replace("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 ", "MySQL syntax error at ")
self.msg_log(s)
if sys.exc_value[0] == 2013:
# lost connection
self.close()
return False
if not check_use: return True
match = re.match("(?is)^([ \r\n\t]*|#[^\n]*)*(use[ \r\n\t]*).*", query)
if match:
dbname = query[match.end(2):].strip("`; \t\r\n")
print "use db: '%s'" % dbname
self._use_db(dbname, False)
# reexecute to reset field_count and so on...
self.handle.query(query)
return True
def _use_db(self, name, do_query=True):
if self.current_db and name == self.current_db.name: return
if do_query: self.query("use `%s`" % name, False)
try:
self.current_db = self.databases[name]
except KeyError:
print "Warning: used an unknown database %r! please refresh host!\n%s" % (name, "".join(traceback.format_stack()))
def select_database(self, db):
self._use_db(db.name)
def refresh(self):
self.query("show databases")
result = self.handle.store_result()
old = dict(self.databases)
db_id = len(old)
for row in result.fetch_row(0):
if not row[0] in old:
self.databases[row[0]] = mysql_db(self, row[0])
else:
del old[row[0]]
for db in old.keys():
print "remove database", db
del self.databases[db]
def refresh_processlist(self):
if not self.query("show processlist"): return
result = self.handle.store_result()
self.processlist = (result.describe(), result.fetch_row(0))
def insert_id(self):
return self.handle.insert_id()
def escape(self, s):
if s is None:
return s
return self.handle.escape_string(s)
emma-0.6/emmalib/mysql_query_tab.py 0000644 0001750 0001750 00000011464 10526154200 015621 0 ustar flo flo # -*- coding: utf-8 -*-
# emma
#
# Copyright (C) 2006 Florian Schmidt (flo@fastflo.de)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import pango
import gtk
import traceback
class mysql_query_tab:
def __init__(self, xml, nb):
self.xml = xml
self.nb = nb
renameload = {
"textview": "query_text",
"treeview": "query_view",
"save_result": "save_result",
"save_result_sql": "save_result_sql",
"add_record": "add_record_tool",
"delete_record": "delete_record_tool",
"apply_record": "apply_record_tool",
"local_search": "local_search_button",
"remove_order": "remove_order",
"label": "query_label",
"page": "first_query",
"query_bottom_label": "query_bottom_label",
"query_db_label": "query_db_label",
}
for attribute, xmlname in renameload.iteritems():
self.__dict__[attribute] = xml.get_widget(xmlname)
self.current_host = None
self.current_db = None
self.model = None
self.last_source = None
self.result_info = None
self.append_iter = None
self.save_result_sql.set_sensitive(False)
self.last_path = None
if hasattr(self, "query"):
self.textview.get_buffer().set_text(self.query)
self.last_auto_name = None
def __getstate__(self):
b = self.textview.get_buffer()
d = {
"name": self.nb.get_tab_label_text(self.page),
"query": b.get_text(b.get_start_iter(), b.get_end_iter())
}
print "query will pickle:", d
return d
def auto_rename(self, new_auto_name):
label = self.get_label()
if label is None:
return
if self.last_auto_name is None:
print "no last_auto_name"
label.set_text(new_auto_name)
self.last_auto_name = new_auto_name
return
current_name = label.get_text()
if self.last_auto_name in current_name:
print "setting new %r from old %r" % (new_auto_name, current_name)
label.set_text(current_name.replace(self.last_auto_name, new_auto_name))
self.last_auto_name = new_auto_name
else:
print "last auto name %r not in %r!" % (self.last_auto_name, current_name)
return
def get_label(self):
tab_widget = self.nb.get_tab_label(self.page)
if not tab_widget:
print "no tab widget"
return
labels = filter(lambda w: type(w) == gtk.Label, tab_widget.get_children())
if not labels:
print "no label found!"
return
return labels[0]
def user_rename(self, new_name):
tab_widget = self.nb.get_tab_label(self.page)
label = self.get_label()
label.set_text(new_name)
def destroy(self):
# try to free some memory
if self.model: self.model.clear()
self.textview.get_buffer().set_text("")
del self.treeview
del self.model
del self.textview
self.treeview = None
self.model = None
self.textview = None
self.update_db_label()
def set(self, text):
self.last_source = text
self.textview.get_buffer().set_text(text)
def update_db_label(self):
h = self.current_host
d = self.current_db
if not h:
self.query_db_label.set_label("no host/database selected")
return
title = "selected host"
if d:
dname = "/" + d.name
title = "selected database"
else:
dname = ""
if h.name == h.host:
hname = h.name
else:
hname = "%s(%s)" % (h.name, h.host)
self.query_db_label.set_label("%s: %s@%s%s" % (
title,
h.user, hname,
dname
))
self.auto_rename("%s%s" % (h.name, dname))
def set_current_host(self, host):
if self.current_host == host and host is not None and self.current_db == host.current_db:
return
self.current_host = host
if host:
self.current_db = host.current_db
else:
self.current_db = None
self.update_db_label()
def set_current_db(self, db):
self.current_host = db.host
self.current_db = db
self.update_db_label()
def update_bottom_label(self):
self.query_bottom_label.set_label("encoding: %s" % self.encoding)
def set_query_encoding(self, encoding):
self.encoding = encoding
self.update_bottom_label()
def set_query_font(self, font_name):
self.textview.get_pango_context()
fd = pango.FontDescription(font_name)
self.textview.modify_font(fd)
def set_result_font(self, font_name):
self.treeview.get_pango_context()
fd = pango.FontDescription(font_name)
self.treeview.modify_font(fd)
def set_wrap_mode(self, wrap):
self.textview.set_wrap_mode(wrap)
emma-0.6/emmalib/mysql_table.py 0000644 0001750 0001750 00000005416 10516735704 014732 0 ustar flo flo # -*- coding: utf-8 -*-
# emma
#
# Copyright (C) 2006 Florian Schmidt (flo@fastflo.de)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import sys, time
class mysql_table:
def __init__(self, db, props, props_description):
self.handle = db.handle
self.host = db.host
self.db = db
self.props = props
self.props_dict = dict(zip(props_description, props))
self.name = props[0]
self.fields = {}
self.field_order = []
self.expanded = False
self.last_field_read = 0
self.create_table = ""
self.describe_headers = []
def __getstate__(self):
d = dict(self.__dict__)
for i in ["handle"]:
del d[i]
#print "table will pickle:", d
return d
def __getitem__(self, what):
try:
return self.props_dict[what]
except:
pass
print "property", what, "not found in table props:", self.props_dict
def refresh(self, refresh_props=True):
self.db.host.select_database(self.db)
if refresh_props:
self.host.query("show table status like '%s'" % self.name)
result = self.handle.store_result()
rows = result.fetch_row(0)
self.props = rows[0]
self.props_dict = dict(zip(map(lambda v: v[0], result.describe()), rows[0]))
self.name = self.props[0]
self.host.query("describe `%s`" % self.name)
result = self.handle.store_result()
self.describe_headers = []
for h in result.describe():
self.describe_headers.append(h[0])
self.fields = {}
self.field_order = []
for row in result.fetch_row(0):
self.field_order.append(row[0])
self.fields[row[0]] = row
self.last_field_read = time.time()
return
def __str__(self):
output = ""
for h, p in zip(self.db.status_headers, self.props):
output += "\t%-25.25s: %s\n" % (h, p)
return output
def get_create_table(self):
if not self.create_table:
self.db.host.select_database(self.db)
self.host.query("show create table `%s`" % self.name)
print "create with:", self.handle
result = self.handle.store_result()
if not result:
print "can't get create table for %s at %s and %s" % (self.name, self, self.handle)
return ""
result = result.fetch_row(0)
self.create_table = result[0][1]
return self.create_table
emma-0.6/icons/ 0000755 0001750 0001750 00000000000 10526165411 011534 5 ustar flo flo emma-0.6/icons/db.png 0000644 0001750 0001750 00000000435 10517472061 012633 0 ustar flo flo PNG
IHDR (-S gAMA 7 tEXtSoftware Adobe ImageReadyqe<