#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#




"""Python DB-API (PEP 249) interface to SQL Service.

http://www.python.org/dev/peps/pep-0249/
"""


import collections
import datetime
import exceptions
import time
import types

from google.storage.speckle.proto import client_error_code_pb2
from google.storage.speckle.proto import client_pb2
from google.storage.speckle.proto import jdbc_type
from google.storage.speckle.proto import sql_pb2
from google.storage.speckle.python import api
from google.storage.speckle.python.api import converters




__path__ = api.__path__








OAUTH_CREDENTIALS_PATH = '~/.googlesql_oauth2.dat'






apilevel = '2.0'

threadsafety = 1



paramstyle = 'format'



version_info = (1, 2, 2, 'final', 0)







class Warning(StandardError, exceptions.Warning):
  pass

class Error(StandardError):
  pass

class InterfaceError(Error):
  pass

class DatabaseError(Error):
  pass

class DataError(DatabaseError):
  pass

class OperationalError(DatabaseError):
  pass

class IntegrityError(DatabaseError):
  pass

class InternalError(DatabaseError):
  pass

class ProgrammingError(DatabaseError):
  pass

class NotSupportedError(DatabaseError):
  pass

Blob = converters.Blob



def Date(year, month, day):
  return datetime.date(year, month, day)

def Time(hour, minute, second):
  return datetime.time(hour, minute, second)

def Timestamp(year, month, day, hour, minute, second):
  return datetime.datetime(year, month, day, hour, minute, second)

def DateFromTicks(ticks):
  return Date(*time.localtime(ticks)[:3])

def TimeFromTicks(ticks):
  return Time(*time.localtime(ticks)[3:6])

def TimestampFromTicks(ticks):
  return Timestamp(*time.localtime(ticks)[:6])

def Binary(string):
  return Blob(string)


STRING = unicode
BINARY = Blob
NUMBER = float
DATETIME = datetime.datetime
ROWID = int


_PYTHON_TYPE_TO_JDBC_TYPE = {
    types.IntType: jdbc_type.INTEGER,
    types.LongType: jdbc_type.INTEGER,
    types.FloatType: jdbc_type.DOUBLE,
    types.BooleanType: jdbc_type.BOOLEAN,
    types.StringType: jdbc_type.VARCHAR,
    types.UnicodeType: jdbc_type.VARCHAR,
    datetime.date: jdbc_type.DATE,
    datetime.datetime: jdbc_type.TIMESTAMP,
    datetime.time: jdbc_type.TIME,
    converters.Blob: jdbc_type.BLOB,
    }


def _ConvertFormatToQmark(statement, args):
  """Replaces '%s' or '%(name)s' with '?'.

  The server actually supports '?' for bind parameters, but the
  MySQLdb implementation of PEP 249 uses 'format' paramstyle (%s) when the
  given args list is a sequence, and 'pyformat' paramstyle (%(name)s) when the
  args list is a mapping.  Most clients don't bother checking the paramstyle
  member and just hardcode '%s' or '%(name)s' in their statements.  This
  function converts a (py)format-style statement into a qmark-style statement.

  Args:
    statement: A string, a SQL statement.
    args: A sequence of arguments matching the statement's bind variables,
        if any.

  Returns:
    The converted string.
  """
  if isinstance(args, dict):
    return statement % collections.defaultdict(lambda: '?')
  elif args:
    qmarks = tuple('?' * len(args))
    return statement % qmarks
  return statement


class _AccessLogger(object):
  """Simple dict-like object that records all lookup attempts.

  Attributes:
    accessed_keys: List of all lookup keys, in the order which they occurred.
  """

  def __init__(self):
    self.accessed_keys = []

  def __getitem__(self, key):
    self.accessed_keys.append(key)
    return ''


def _ConvertArgsDictToList(statement, args):
  """Convert a given args mapping to a list of positional arguments.

  Takes a statement written in 'pyformat' style which uses mapping keys from
  the given args mapping, and returns the list of args values that would be
  used for interpolation if the statement were written in a positional
  'format' style instead.

  For example, consider the following pyformat string and a mapping used for
  interpolation:

    '%(foo)s '%(bar)s' % {'foo': 1, 'bar': 2}

  Given these parameters, this function would return the following output:

    [1, 2]

  This could then be used for interpolation if the given string were instead
  expressed using a positional format style:

    '%s %s' % (1, 2)

  Args:
    statement: The statement, possibly containing pyformat style tokens.
    args: Mapping to pull values from.

  Returns:
    A list containing values from the given args mapping.
  """
  access_logger = _AccessLogger()
  statement % access_logger
  return [args[key] for key in access_logger.accessed_keys]


class Cursor(object):

  def __init__(self, conn, use_dict_cursor=False):
    """Initializer.

    Args:
      conn: A Connection object.
      use_dict_cursor: Optional boolean to convert each row of results into a
          dictionary. Defaults to False.
    """
    self._conn = conn
    self._description = None
    self._rowcount = -1
    self.arraysize = 1
    self._open = True
    self.lastrowid = None
    self._use_dict_cursor = use_dict_cursor

  @property
  def description(self):
    return self._description

  @property
  def rowcount(self):
    return self._rowcount

  def close(self):
    """Marks the cursor as unusable for further operations."""
    self._CheckOpen()
    self._open = False

  def _GetJdbcTypeForArg(self, arg):
    """Get the JDBC type which corresponds to the given Python object type."""
    arg_jdbc_type = _PYTHON_TYPE_TO_JDBC_TYPE.get(type(arg))
    if arg_jdbc_type:
      return arg_jdbc_type


    for python_t, jdbc_t in _PYTHON_TYPE_TO_JDBC_TYPE.items():
      if isinstance(arg, python_t):
        return jdbc_t



    try:
      return self._GetJdbcTypeForArg(arg[0])
    except TypeError:


      raise TypeError('unknown type')


  def _EncodeVariable(self, arg):
    """Converts a variable to a type and value.

    Args: