<commit_before>#!/usr/bin/python

# Copyright (c) 2009, Purdue University
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice, this
# list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
# 
# Neither the name of the Purdue University nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""This module is an API to access the dnsManagement database.

This module should only be run by servers with authentication layers
that are active. This module does not include authentication, but does
include authorization.

The api that will be exposed by this module is meant for use in a web
application or rpc server. This module is not for use in command line tools.

The two primary uses of this class are: 
1. to use convience functions to get large amounts of data out of the db 
  without large amounts of db queries. For usage on this consult the pydoc
  on the individual functions.

2. to Make/Remove/List rows in the database. The method that is used in this
  class is based on generic Make/Remove/Lock functions that take specifc
  dictionaries that correspond to the table that is being referenced. 

  Here is an example of how to remove rows from the acls table:

  acls_dict = db_instance.GetEmptyRowDict('acls')
  acls_dict['acl_name'] = 'test_acl'
  db_instance.StartTransaction()
  try:
    matching_rows = db_instance.ListRow('acls', acls_dict)
    for row in matching_rows:
      db_instance.RemoveRow('acls', row)
  except Exception:
    db_instance.RollbackTransaction()
  else:
    db_instance.CommitTransaction()

Note: MySQLdb.Error can be raised in almost any function in this module. Please
      keep that in mind when using this module.
"""

__copyright__ = 'Copyright (C) 2009, Purdue University'
__license__ = 'BSD'
__version__ = '#TRUNK#'


import datetime
import Queue
import threading
import time
import uuid

import MySQLdb
import MySQLdb.cursors

import constants
import data_validation
import errors
import table_enumeration


class TransactionError(errors.DbAccessError):
  pass


class UnexpectedDataError(data_validation.UnexpectedDataError):
  pass


class InvalidInputError(data_validation.InvalidInputError):
  pass


class MissingDataTypeError(data_validation.MissingDataTypeError):
  pass


class dbAccess(object):
  """This class provides the primary interface for connecting and interacting
   with the roster database.
   """

  def __init__(self, db_host, db_user, db_passwd, db_name, big_lock_timeout,
               big_lock_wait, thread_safe=True):
    """Instantiates the db_access class.

    Inputs:
      db_host: string of the database host name
      db_user: string of the user name used to connect to mysql
      db_passwd: string of password used to connect to mysql
      db_name: string of name of database in mysql server to use
      big_lock_timeout: integer of how long the big lock should be valid for
      big_lock_wait: integer of how long to wait for proccesses to finish
                     before locking the database
      thread_safe: boolean of if db_acceess should be thread safe
    """
    # Do some better checking of these args
    self.db_host = db_host
    self.db_user = db_user
    self.db_passwd = <PASSWORD>
    self.db_name = db_name
    self.big_lock_timeout = big_lock_timeout
    self.big_lock_wait = big_lock_wait
    self.transaction_init = False
    self.connection = None
    self.cursor = None
    # This is generated only when ListRow is called and is then cached for
    # the life of the object.
    self.foreign_keys = []
    self.data_validation_instance = None
    self.locked_db = False
    self.thread_safe = thread_safe
    self.queue = Queue.Queue()
    self.now_serving = None
    self.queue_update_lock = threading.Lock()
    
  def StartTransaction(self):
    """Starts a transaction.

    Also it starts a db connection if none exists or it times out.
    Always creates a new cursor.
    
    This function also serializes all requests on this object and if the 
    big lock has been activated will wait for it to be released.

    Raises:
      TransactionError: Cannot start new transaction last transaction not
                        committed or rolled-back.
    """
    if( self. thread_safe ):
      unique_id = uuid.uuid4()
      self.queue.put(unique_id)

      while_sleep = 0
      while( unique_id != self.now_serving ):
        time.sleep(while_sleep)
        self.queue_update_lock.acquire()
        if( self.now_serving is None ):<commit_msg>Turns out I should have kept this. It is useful in the unittests for starting new connections.<commit_after>#!/usr/bin/python

# Copyright (c) 2009, Purdue University
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice, this
# list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
# 
# Neither the name of the Purdue University nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""This module is an API to access the dnsManagement database.

This module should only be run by servers with authentication layers
that are active. This module does not include authentication, but does
include authorization.

The api that will be exposed by this module is meant for use in a web
application or rpc server. This module is not for use in command line tools.

The two primary uses of this class are: 
1. to use convience functions to get large amounts of data out of the db 
  without large amounts of db queries. For usage on this consult the pydoc
  on the individual functions.

2. to Make/Remove/List rows in the database. The method that is used in this
  class is based on generic Make/Remove/Lock functions that take specifc
  dictionaries that correspond to the table that is being referenced. 

  Here is an example of how to remove rows from the acls table:

  acls_dict = db_instance.GetEmptyRowDict('acls')
  acls_dict['acl_name'] = 'test_acl'
  db_instance.StartTransaction()
  try:
    matching_rows = db_instance.ListRow('acls', acls_dict)
    for row in matching_rows:
      db_instance.RemoveRow('acls', row)
  except Exception:
    db_instance.RollbackTransaction()
  else:
    db_instance.CommitTransaction()

Note: MySQLdb.Error can be raised in almost any function in this module. Please
      keep that in mind when using this module.
"""

__copyright__ = 'Copyright (C) 2009, Purdue University'
__license__ = 'BSD'
__version__ = '#TRUNK#'


import datetime
import Queue
import threading
import time
import uuid

import MySQLdb
import MySQLdb.cursors

import constants
import data_validation
import errors
import table_enumeration


class TransactionError(errors.DbAccessError):
  pass


class UnexpectedDataError(data_validation.UnexpectedDataError):
  pass


class InvalidInputError(data_validation.InvalidInputError):
  pass


class MissingDataTypeError(data_validation.MissingDataTypeError):
  pass


class dbAccess(object):
  """This class provides the primary interface for connecting and interacting
   with the roster database.
   """

  def __init__(self, db_host, db_user, db_passwd, db_name, big_lock_timeout,
               big_lock_wait, thread_safe=True):
    """Instantiates the db_access class.

    Inputs:
      db_host: string of the database host name
      db_user: string of the user name used to connect to mysql
      db_passwd: string of password used to connect to mysql
      db_name: string of name of database in mysql server to use
      big_lock_timeout: integer of how long the big lock should be valid for
      big_lock_wait: integer of how long to wait for proccesses to finish
                     before locking the database
      thread_safe: boolean of if db_acceess should be thread safe
    """
    # Do some better checking of these args
    self.db_host = db_host
    self.db_user = db_user
    self.db_passwd = <PASSWORD>
    self.db_name = db_name
    self.big_lock_timeout = big_lock_timeout
    self.big_lock_wait = big_lock_wait
    self.transaction_init = False
    self.connection = None
    self.cursor = None
    # This is generated only when ListRow is called and is then cached for
    # the life of the object.
    self.foreign_keys = []
    self.data_validation_instance = None
    self.locked_db = False
    self.thread_safe = thread_safe
    self.queue = Queue.Queue()
    self.now_serving = None
    self.queue_update_lock = threading.Lock()

  def close(self):
    """Closes a connection that has been opened. 

    A new connection will be created on StartTransaction.
    """
    if( self.connection is not None ):
      self.connection.close()
    self.connection = None
    
  def StartTransaction(self):
    """Starts a transaction.

    Also it starts a db connection if none exists or it times out.
    Always creates a new cursor.
    
    This function also serializes all requests on this object and if the 
    big lock has been activated will wait for it to be released.

    Raises:
      TransactionError: Cannot start new transaction last transaction not
                        committed or rolled-back.
    """
    if( self. thread_safe ):
      unique_id = uuid.uuid4()
      self.queue.put(unique_id)

      while_sleep = 0
      while( unique_id != self.now_serving ):
        time.sleep(while_sleep)
        self.queue_update_lock.acquire()
        if( self.now_serving is None ):
{'fraction_non_alphanumeric': 0.05293918346603422, 'fraction_numerical': 0.002032864645095714, 'mean_word_length': 3.6631121642969986, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4106924', 'n_tokens_mistral': 3252, 'n_tokens_neox': 3068, 'n_words': 1431}