Blame db/connection.py

Ivan Mahonin cbf076
Ivan Mahonin cbf076
import datetime
Ivan Mahonin cbf076
import traceback
Ivan Mahonin 4656ad
import MySQLdb.cursors
Ivan Mahonin 4656ad
Ivan Mahonin 4656ad
Ivan Mahonin 4656ad
class Cursor:
Ivan Mahonin 4656ad
  def __init__(self, connection, internal):
Ivan Mahonin 4656ad
    self.connection = connection
Ivan Mahonin 4656ad
    self.internal = internal
Ivan Mahonin 4656ad
    self.entered = None
Ivan Mahonin 4656ad
    self.iterator = None
Ivan Mahonin 4656ad
Ivan Mahonin 4656ad
  def get_internal(self):
Ivan Mahonin 4656ad
    return self.internal if self.entered is None else self.entered
Ivan Mahonin 4656ad
Ivan Mahonin 4656ad
  def __enter__(self):
Ivan Mahonin 4656ad
    self.entered = self.internal.__enter__()
Ivan Mahonin 4656ad
    return self
Ivan Mahonin 4656ad
Ivan Mahonin 4656ad
  def __exit__(self, exc_type, exc_value, traceback):
Ivan Mahonin 4656ad
    self.entered.__exit__(exc_type, exc_value, traceback)
Ivan Mahonin 4656ad
    self.entered = None
Ivan Mahonin 4656ad
Ivan Mahonin 4656ad
  def __iter__(self):
Ivan Mahonin 4656ad
    self.iterator = iter(self.get_internal())
Ivan Mahonin 4656ad
    return self
Ivan Mahonin 4656ad
Ivan Mahonin 4656ad
  def __next__(self):
Ivan Mahonin 4656ad
    data = next(self.iterator)
Ivan Mahonin 4656ad
    if type(data) is dict:
Ivan Mahonin 4656ad
      for k, v in data.items():
Ivan Mahonin b838e2
        t = self.connection.typebytype.get( type(v) )
Ivan Mahonin 4656ad
        if t: data[k] = t.from_db(self, v)
Ivan Mahonin 4656ad
    else:
Ivan Mahonin 4656ad
      orig = data
Ivan Mahonin 4656ad
      data = list()
Ivan Mahonin 4656ad
      for v in orig:
Ivan Mahonin b838e2
        t = self.connection.typebytype.get( type(v) )
Ivan Mahonin 4656ad
        data.append(t.from_db(self, v) if t else v)
Ivan Mahonin 4656ad
    return data
Ivan Mahonin 4656ad
  
Ivan Mahonin 05525d
  def execute(self, sql, *args, **kvargs):
Ivan Mahonin b838e2
    sql = self.connection.parse(sql, *args, **kvargs)
Ivan Mahonin b838e2
    try:
Ivan Mahonin b838e2
      self.get_internal().execute( sql )
Ivan Mahonin b838e2
    except Exception as e:
Ivan Mahonin b838e2
      print('SQL Error in query:')
Ivan Mahonin b838e2
      print(sql)
Ivan Mahonin b838e2
      raise e
Ivan Mahonin cbf076
Ivan Mahonin cbf076
Ivan Mahonin cbf076
class Connection:
Ivan Mahonin cbf076
  def __init__(self, pool, internal, readonly = True):
Ivan Mahonin cbf076
    self.pool = pool
Ivan Mahonin b838e2
    self.server = self.pool.server
Ivan Mahonin b838e2
    self.typebytype = self.server.dbtypebytype
Ivan Mahonin b838e2
    self.typebychar = self.server.dbtypebychar
Ivan Mahonin b838e2
    self.cache = self.server.dbcache.create_connection(self)
Ivan Mahonin 05525d
    self.request = None
Ivan Mahonin cbf076
    self.internal = internal
Ivan Mahonin cbf076
    self.readonly = readonly
Ivan Mahonin cbf076
    self.finished = True
Ivan Mahonin cbf076
    self.begin()
Ivan Mahonin e5b0ac
    self.on_commit = list()
Ivan Mahonin e5b0ac
    self.on_rollback = list()
Ivan Mahonin cbf076
Ivan Mahonin b838e2
  def parse(self, text, *args, **kvargs):
Ivan Mahonin b838e2
    i = iter(text)
Ivan Mahonin b838e2
    index = 0
Ivan Mahonin b838e2
    result = ''
Ivan Mahonin b838e2
    try:
Ivan Mahonin b838e2
      while True:
Ivan Mahonin b838e2
        try: c = next(i)
Ivan Mahonin b838e2
        except StopIteration: break
Ivan Mahonin b838e2
        if c == '%':
Ivan Mahonin b838e2
          c = next(i)
Ivan Mahonin b838e2
          if c == '(':
Ivan Mahonin b838e2
            field = ''
Ivan Mahonin b838e2
            while True:
Ivan Mahonin b838e2
              c = next(i)
Ivan Mahonin b838e2
              if c == ')': break
Ivan Mahonin b838e2
              field += c
Ivan Mahonin b838e2
            c = next(i)
Ivan Mahonin b838e2
            result += self.typebychar[c].to_db(self, kvargs[field])
Ivan Mahonin b838e2
          elif c == '%':
Ivan Mahonin b838e2
            result += '%'
Ivan Mahonin b838e2
          else:
Ivan Mahonin b838e2
            result += self.typebychar[c].to_db(self, args[index])
Ivan Mahonin b838e2
            index += 1
Ivan Mahonin b838e2
        else:
Ivan Mahonin b838e2
          result += c
Ivan Mahonin b838e2
    except StopIteration:
Ivan Mahonin b838e2
      raise Exception('unexpeted end of sql template')
Ivan Mahonin b838e2
    return result
Ivan Mahonin b838e2
Ivan Mahonin 05525d
  def cursor(self, as_dict = False, sql = None, *args, **kvargs):
Ivan Mahonin cbf076
    assert not self.finished
Ivan Mahonin 4656ad
    cursorclass = MySQLdb.cursors.DictCursor if as_dict else MySQLdb.cursors.Cursor
Ivan Mahonin 4656ad
    cursor = Cursor(self, self.internal.cursor(cursorclass))
Ivan Mahonin 05525d
    if sql:
Ivan Mahonin 05525d
      cursor.execute(sql, *args, **kvargs)
Ivan Mahonin 4656ad
    return cursor
Ivan Mahonin 05525d
  def cursor_list(self, sql = None, *args, **kvargs):
Ivan Mahonin 05525d
    return self.cursor(False, sql, *args, **kvargs)
Ivan Mahonin 05525d
  def cursor_dict(self, sql = None, *args, **kvargs):
Ivan Mahonin 05525d
    return self.cursor(True, sql, *args, **kvargs)
Ivan Mahonin 4656ad
  
Ivan Mahonin 4656ad
  def query(self, as_dict, sql = None, *args, **kvargs):
Ivan Mahonin 05525d
    with self.cursor(as_dict, sql, *args, **kvargs) as cursor:
Ivan Mahonin 4656ad
      return list(cursor)
Ivan Mahonin 4656ad
  def query_list(self, sql = None, *args, **kvargs):
Ivan Mahonin 4656ad
    return self.query(False, sql, *args, **kvargs)
Ivan Mahonin 4656ad
  def query_dict(self, sql = None, *args, **kvargs):
Ivan Mahonin 4656ad
    return self.query(True, sql, *args, **kvargs)
Ivan Mahonin 4656ad
Ivan Mahonin 4656ad
  def execute(self, sql = None, *args, **kvargs):
Ivan Mahonin 05525d
    with self.cursor(False, sql, *args, **kvargs):
Ivan Mahonin 05525d
      return
Ivan Mahonin cbf076
Ivan Mahonin 05525d
  def insert_id(self):
Ivan Mahonin cbf076
    assert not self.finished
Ivan Mahonin 05525d
    return self.internal.insert_id()
Ivan Mahonin cbf076
  
Ivan Mahonin cbf076
  def escape(self, *args, **kwargs):
Ivan Mahonin cbf076
    r = self.internal.escape(*args, **kwargs)
Ivan Mahonin cbf076
    return r.decode("utf8") if type(r) is bytes else r
Ivan Mahonin cbf076
Ivan Mahonin cbf076
  def escape_string(self, *args, **kwargs):
Ivan Mahonin cbf076
    r = self.internal.escape_string(*args, **kwargs)
Ivan Mahonin cbf076
    return r.decode("utf8") if type(r) is bytes else r
Ivan Mahonin cbf076
  
Ivan Mahonin cbf076
  def begin(self):
Ivan Mahonin cbf076
    assert self.finished
Ivan Mahonin cbf076
    self.finished = False
Ivan Mahonin b838e2
    self.execute("SET sql_mode='STRICT_TRANS_TABLES'")
Ivan Mahonin cbf076
    if self.readonly:
Ivan Mahonin 4656ad
      self.execute("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ")
Ivan Mahonin 4656ad
      self.execute("START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT")
Ivan Mahonin cbf076
    else:
Ivan Mahonin 4656ad
      self.execute("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE")
Ivan Mahonin b838e2
      self.execute("START TRANSACTION READ WRITE")
Ivan Mahonin 4656ad
    self.now = datetime.datetime.now(datetime.timezone.utc)
Ivan Mahonin cbf076
Ivan Mahonin e5b0ac
  def process_events(self, events, skip_errors = False):
Ivan Mahonin e5b0ac
    while events:
Ivan Mahonin e5b0ac
      events_copy = list(events)
Ivan Mahonin e5b0ac
      events.clear()
Ivan Mahonin e5b0ac
      for event in events_copy:
Ivan Mahonin e5b0ac
        try:
Ivan Mahonin e5b0ac
          event[0](*event[1], *event[2])
Ivan Mahonin e5b0ac
        except Exception as e:
Ivan Mahonin e5b0ac
          print("exception in event")
Ivan Mahonin e5b0ac
          print(traceback.format_exc())
Ivan Mahonin e5b0ac
          print(e)
Ivan Mahonin e5b0ac
          if not skip_errors:
Ivan Mahonin e5b0ac
            raise e
Ivan Mahonin e5b0ac
Ivan Mahonin e5b0ac
  def call_on_commit(self, function, *args, **kvargs):
Ivan Mahonin e5b0ac
    self.on_commit.append((function, args, kvargs))
Ivan Mahonin e5b0ac
  def call_on_rollback(self, function, *args, **kvargs):
Ivan Mahonin e5b0ac
    self.on_rollback.append((function, args, kvargs))
Ivan Mahonin e5b0ac
Ivan Mahonin cbf076
  def commit(self):
Ivan Mahonin cbf076
    assert not self.finished
Ivan Mahonin e5b0ac
    self.process_events(self.on_commit)
Ivan Mahonin e5b0ac
    self.on_commit.clear()
Ivan Mahonin e5b0ac
    self.on_rollback.clear()
Ivan Mahonin cbf076
    self.internal.commit()
Ivan Mahonin cbf076
    self.finished = True
Ivan Mahonin cbf076
  
Ivan Mahonin cbf076
  def rollback(self):
Ivan Mahonin cbf076
    assert not self.finished
Ivan Mahonin e5b0ac
    self.process_events(self.on_rollback, skip_errors = True)
Ivan Mahonin e5b0ac
    self.on_commit.clear()
Ivan Mahonin e5b0ac
    self.on_rollback.clear()
Ivan Mahonin cbf076
    self.internal.rollback()
Ivan Mahonin cbf076
    self.finished = True
Ivan Mahonin cbf076
      
Ivan Mahonin cbf076
  def release(self):
Ivan Mahonin cbf076
    if not self.finished:
Ivan Mahonin cbf076
      try: self.rollback()
Ivan Mahonin cbf076
      except Exception as e:
Ivan Mahonin cbf076
        print(traceback.format_exc())
Ivan Mahonin cbf076
        print(e)