diff --git a/py_dbcn/connectors/core/records.py b/py_dbcn/connectors/core/records.py index 479d813f79a124b9939327cb6031ce15bf48e06e..ea585e77c5ef6262e985bb24a74d1d293d97bb6f 100644 --- a/py_dbcn/connectors/core/records.py +++ b/py_dbcn/connectors/core/records.py @@ -53,7 +53,7 @@ class BaseRecords: raise ValueError('Invalid table name of "{0}".'.format(table_name)) # Check that provided SELECT clause is valid format. - select_clause = self._base.validate.sanitize_select_clause(select_clause) + select_clause = self._base.validate.sanitize_select_identifier_clause(select_clause) # Check that provided WHERE clause is valid format. # TODO: Implement proper clause sanitization. diff --git a/py_dbcn/connectors/core/validate.py b/py_dbcn/connectors/core/validate.py index baaba428400398a779f5870879bc04b84d9997e3..9688fe7d7d2eeac710129fd4c7fd039bae0a6ebe 100644 --- a/py_dbcn/connectors/core/validate.py +++ b/py_dbcn/connectors/core/validate.py @@ -7,6 +7,16 @@ Should be inherited by language-specific connectors. # System Imports. import copy, re +from io import StringIO +from tokenize import ( + generate_tokens, + ENDMARKER, + NAME, + NEWLINE, + NUMBER, + OP, + STRING, +) # Internal Imports. from py_dbcn.logging import init_logging @@ -75,8 +85,6 @@ class BaseValidate: # Check acceptable patterns. if is_quoted is False: # Check against "unquoted patterns". - '0-9a-zA-Z$_' - # Check against "quoted patterns". pattern = re.compile('^([0-9a-zA-Z$_])+$') if not re.match(pattern, identifier): return (False, """does not match acceptable characters.\n Identifier is: {0}""".format(identifier)) @@ -149,41 +157,10 @@ class BaseValidate: # Passed checks. return True - def table_column(self, identifier): - """Validates that provided table column uses set of acceptable characters. - - Differs from validate.table_columns() in that this checks a singular column, and that one checks a set. - Aka, validate.table_columns() is a parent method that calls this function. - - :param identifier: Potential column of table to validate. - :return: True if valid | False otherwise. - """ - # Run basic sanitation against provided param. - if identifier is None: - raise TypeError('Invalid table column. Is None.') - identifier = str(identifier).strip() - - # Check if value is quoted. - is_quoted = self._is_quoted(identifier) - - # Validate using "general identifier" logic. - results = self._identifier(identifier) - - if results[0] is False: - if is_quoted: - raise ValueError(u'Invalid table column of {0}. Column {1}'.format(str(identifier), results[1])) - else: - raise ValueError(u'Invalid table column of "{0}". Column {1}'.format(str(identifier), results[1])) - - # Passed checks. - return True - - # endregion Name Validation - def table_columns(self, columns): """Validates that provided column values match expected syntax. - Differs from validate.table_column() in that this checks a set of columns, and that one checks a singular. + Differs from validate.table_column() in that this checks a set of columns, and that function checks a singular. Aka, validate.table_column() is a child method that is called by this function. :param columns: Str or dict of columns to validate. @@ -239,137 +216,195 @@ class BaseValidate: # For now, always return as valid. return columns - # region Clause Validation + def table_column(self, identifier): + """Validates that provided table column uses set of acceptable characters. - def sanitize_select_clause(self, clause): - """ - Validates that provided clause follows acceptable format. - :param clause: SELECT clause to validate. + Differs from validate.table_columns() in that this checks a singular column, and that one checks a set. + Aka, validate.table_columns() is a parent method that calls this function. + + :param identifier: Potential column of table to validate. :return: True if valid | False otherwise. """ - if not self._reserved_function_names: - raise ValueError('Reserved keyword list is not defined.') + # Run basic sanitation against provided param. + if identifier is None: + raise TypeError('Invalid table column. Is None.') + identifier = str(identifier).strip() - if isinstance(clause, list) or isinstance(clause, tuple): - # Format we want. - pass - else: - # Handle for None type. Default to "all". - if clause is None: - clause = '*' - else: - # Handle for all other types. - clause = str(clause) + # Check if value is quoted. + is_quoted = self._is_quoted(identifier) - # Check for outer parens. - if ( - len(clause) > 1 - and ( - (clause[0] == '(' and clause[-1] == ')') - or (clause[0] == '[' and clause[-1] == ']') - ) - ): - clause = clause[1:-1] + # Validate using "general identifier" logic. + results = self._identifier(identifier) - # Convert to list. - clause = clause.split(',') - for index in range(len(clause)): - item = clause[index].strip() - if item == '*': - clause[index] = item - clause[index] = '{0}'.format(item) + if results[0] is False: + if is_quoted: + raise ValueError(u'Invalid table column of {0}. Column {1}'.format(str(identifier), results[1])) + else: + raise ValueError(u'Invalid table column of "{0}". Column {1}'.format(str(identifier), results[1])) - # Handle for "all" star. - if len(clause) == 1 and clause[0] == '*': - return '*' + # Passed checks. + return True - # Validate each item in clause, now that it's an array. - new_clause = [] - for item in clause: - found_functions = False - item = str(item).strip() + # endregion Name Validation - # Strip out function values. - # First check against regex matches. - func_call_regex = (r'\(*|'.join(self._reserved_function_names)) + # region Clause Validation - matches = re.match(func_call_regex, item, flags=re.IGNORECASE) + def sanitize_select_identifier_clause(self, clause): + """ + Validates that provided clause follows acceptable format. + :param clause: SELECT clause to validate. + :return: Properly formatted clause if possible, otherwise error. + """ + if not self._reserved_function_names: + raise ValueError('Reserved keyword list is not defined.') - # Proceed if at least one match is found. - stripped_left = '' - stripped_right = '' - if matches: - index = 0 - while index < len(self._reserved_function_names): - func_call = self._reserved_function_names[index] - if re.match(r'^{0}\('.format(func_call), item, flags=re.IGNORECASE) and item[-1] == ')': - # Found a match. Update identifier and check for further matches. - found_functions = True - length = len(func_call) + 1 - stripped_left += item[:length] - stripped_right += ')' - item = item[length:-1].strip() - index += 1 + # Validate. + return self._inner_sanitize_columns(clause, allow_wildcard=True) - # Error if "all" star. Should not pass this in addition to other values. - if item == '*' and not found_functions: - raise ValueError('SELECT clause provided * with other params. * is only valid alone.') + def sanitize_where_clause(self, clause): + """ + Validates that provided clause follows acceptable format. + :param clause: WHERE clause to validate. + :return: Properly formatted clause if possible, otherwise error. + """ + # TODO: Implement proper sanitization. - # Validate individual identifier. - if item != '*': - results = self._identifier(item) - if results[0] is False: - raise ValueError('Invalid SELECT identifier. Identifier {0}'.format(results[1])) + # Handle if none. + if clause is None: + clause = '' - # If we made it this far, item is valid. Escape with backticks and readd. - is_quoted = self._is_quoted(item) - if is_quoted: - # Was already quoted, but may not be with expected format. Reformat to guaranteed use expected format. - item = '{1}{0}{1}'.format(item[1:-1], self._quote_identifier_format) - elif item == '*': - pass - else: - # Was not quoted. Add quotes. - item = '{1}{0}{1}'.format(item, self._quote_identifier_format) + # Convert to str. + clause = str(clause).strip() - # Re-add function values. - item = stripped_left.upper() + item + stripped_right + # Remove prefix, if present. + if clause.lower().startswith('where'): + clause = clause[5:] - # Append updated value to clause. - new_clause.append(item) + # Strip now that prefix is gone. + clause = clause.strip() - # All items in clause were valid. Re-concatenate into single expected str format. - clause = ', '.join(new_clause) + # Put into expected format. + if len(clause) > 1: + clause = '\nWHERE {0}'.format(clause) - # Return validated and sanitized SELECT clause. return clause - def columns_clause(self, clause): + def sanitize_columns_clause(self, clause): """ Validates that provided clause follows acceptable format. :param clause: COLUMNS clause to validate. - :return: True if valid | False otherwise. + :return: Properly formatted clause if possible, otherwise error. """ - # For now, always return as valid. - return True + if not self._reserved_function_names: + raise ValueError('Reserved keyword list is not defined.') + + # Validate. + return self._inner_sanitize_columns(clause, allow_wildcard=False) - def values_clause(self, clause): + def sanitize_values_clause(self, clause): """ Validates that provided clause follows acceptable format. + :param clause: VALUES clause to validate. - :return: True if valid | False otherwise. + :return: Properly formatted clause if possible, otherwise error. """ # For now, always return as valid. - return True + return clause - def where_clause(self, clause): - """ - Validates that provided clause follows acceptable format. - :param clause: WHERE clause to validate. - :return: True if valid | False otherwise. - """ - # For now, always return as valid. - return True + # # TODO: Attempted to have full, dynamic validation of entire clause and all inner values. + # # However, had too many cases where it would break, due to being able to essentially put in anything. + # # It may be possible to magically validate the "values clause", but it's too much time/work + # # to implement right now. Potentially look into again in the future. + # # + # # Also, due to the nature of this logic, it almost seems like this should either do the + # # "full dynamic validation" or have no validation at all for this clause. Unsure how best to handle this + # # at the current date... + # # + # print('\n\n\n\n') + # print('orig clause: "{0}"'.format(clause)) + # + # if not self._reserved_function_names: + # raise ValueError('Reserved keyword list is not defined.') + # + # # Convert to array format. + # if isinstance(clause, list) or isinstance(clause, tuple): + # # Format we want. + # pass + # else: + # print('as str: {0}') + # + # # Handle for None type. + # if clause is None: + # clause = '' + # else: + # clause = str(clause).strip() + # + # # Remove clause starting value. + # if clause.lower().startswith('values'): + # clause = clause[6:].strip() + # + # # Ensure not empty when prefix was provided. + # if len(clause) < 1: + # raise ValueError('Invalid VALUES clause. Must have one or more items.') + # + # # Due to possible amount of variation (particularly in quoted string values), + # # we have to tokenize in order to attempt to properly parse. + # # Via tokenization, we can effectively avoid outer parens. + # tokens = generate_tokens(StringIO(clause).readline) + # print('\nas tokens:') + # clause = [] + # for token in tokens: + # print(' {0}'.format(token)) + # if token.exact_type == 1: + # # Direct value, missing quotes. + # token = str(token.string).strip() + # clause.append('{0}{1}{0}'.format(self._quote_str_literal_format, token)) + # elif token.exact_type == 3: + # # String type. Convert to expected quote type. + # token = str(token.string).strip()[1:-1] + # clause.append('{0}{1}{0}'.format(self._quote_str_literal_format, token)) + # + # print('as array: {0}'.format(clause)) + # + # # Loop through each item in array and ensure proper formatting. + # updated_clause = [] + # for index in range(len(clause)): + # item = clause[index] + # print(' type: {0} val: {1}'.format(type(item), item)) + # + # # Sanitize for null. + # if item is None: + # item = 'Null' + # + # # Sanitize str outer quoting. + # elif isinstance(item, str): + # print(' formatted: {0}'.format("""{0}""".format(item))) + # + # # TODO: Unsure of if this part is required? Seemed to be having issues parsing when multiple quotes are + # # put in a row. + # tokens = generate_tokens(StringIO("""{0}""".format(item)).readline) + # print(' tokens:') + # for token in tokens: + # print(' {0}'.format(token)) + # + # if len(item) > 1: + # if item[0] != self._quote_str_literal_format or item[-1] != self._quote_str_literal_format: + # item = '{0}{1}{0}'.format(self._quote_str_literal_format, item) + # + # # Set all others to str equivalent. + # else: + # item = str(item) + # + # # Append item. + # updated_clause.append(item) + # clause = updated_clause + # + # # Handle empty clause. + # if clause == '': + # return '' + # + # # Return formatted clause. + # return ' VALUES ({0})'.format(', '.join(clause)) def sanitize_order_by_clause(self, clause): """ @@ -377,32 +412,30 @@ class BaseValidate: :param clause: ORDER_BY clause to validate. :return: Properly formatted clause if possible, otherwise error. """ - # TODO: Implement proper sanitization checks. - - if clause is None: - clause = '' + if not self._reserved_function_names: + raise ValueError('Reserved keyword list is not defined.') - # Strip any potential whitespace. - clause = str(clause).strip() + # Quickly sanitize if string format. + if isinstance(clause, str): + clause = clause.strip() - # Handle if clause is not empty. - if clause != '': - # Remove prefix, if present. + # Remove clause starting value. if clause.lower().startswith('order by'): - clause = clause[8:] + clause = clause[8:].strip() - # Strip again, with prefix removed. - clause = clause.strip() + # Ensure not empty when prefix was provided. + if len(clause) < 1: + raise ValueError('Invalid ORDER BY clause.') - # Prevent wildcard use. - if clause == '*': - raise ValueError('ORDER BY clause cannot use wildcard.') + # Validate. + clause = self._inner_sanitize_columns(clause, allow_wildcard=False) - # Put in expected format. - clause = ' ORDER BY {0}'.format(clause) + # Handle empty clause. + if clause == '': + return '' # Return formatted clause. - return clause + return '\nORDER BY {0}'.format(clause) def sanitize_limit_clause(self, clause): """ @@ -410,8 +443,6 @@ class BaseValidate: :param clause: LIMIT clause to validate. :return: Properly formatted clause if possible, otherwise error. """ - # TODO: Implement proper sanitization checks. - if clause is None: clause = '' @@ -427,6 +458,16 @@ class BaseValidate: # Strip again, with prefix removed. clause = clause.strip() + # Check that value can be safely converted to int. + try: + clause = int(clause) + except ValueError: + raise ValueError('The LIMIT clause expects a positive integer.') + + # Verify is a positive integer. + if clause < 1: + raise ValueError('The LIMIT clause must return at least one record.') + # Put in expected format. clause = ' LIMIT {0}'.format(clause) @@ -435,6 +476,8 @@ class BaseValidate: # endregion Clause Validation + # region Helper Functions + def _is_quoted(self, value): """Checks if provided value is quoted. @@ -451,3 +494,124 @@ class BaseValidate: is_quoted = True return is_quoted + + def _inner_sanitize_columns(self, clause, allow_wildcard=False): + """""" + if allow_wildcard: + quote_format = self._quote_identifier_format + else: + quote_format = self._quote_column_format + + # Convert to array format. + if isinstance(clause, list) or isinstance(clause, tuple): + # Format we want. + pass + else: + + # Handle for None type. + if clause is None: + # If wildcards allowed, then default to "all". + if allow_wildcard: + clause = '*' + else: + # Wildcard not allowed. Empty instead. + clause = '' + else: + # Handle for all other types. + clause = str(clause).strip() + + # Check for outer parens. + if ( + len(clause) > 1 + and ( + (clause[0] == '(' and clause[-1] == ')') + or (clause[0] == '[' and clause[-1] == ']') + ) + ): + clause = clause[1:-1] + + # Convert to list. + clause = clause.split(',') + for index in range(len(clause)): + clause[index] = str(clause[index]).strip() + + # Remove potential trailing deadspace. + if len(clause) > 1 and clause[-1] == '': + clause = clause[:-1] + + # Check if "all" star wildcard is allowed. + if allow_wildcard: + # Handle for SELECT all. + if len(clause) == 1 and clause[0] == '*': + return '*' + # Handle for empty otherwise. + elif len(clause) == 1 and clause[0] == '': + return '' + + # Validate each item in clause, now that it's an array. + new_clause = [] + for item in clause: + found_functions = False + item = str(item).strip() + + # Strip out function values. + # First check against regex matches. + func_call_regex = (r'\(*|'.join(self._reserved_function_names)) + + matches = re.match(func_call_regex, item, flags=re.IGNORECASE) + + # Proceed if at least one match is found. + stripped_left = '' + stripped_right = '' + if matches: + index = 0 + while index < len(self._reserved_function_names): + func_call = self._reserved_function_names[index] + if re.match(r'^{0}\('.format(func_call), item, flags=re.IGNORECASE) and item[-1] == ')': + # Found a match. Update identifier and check for further matches. + found_functions = True + length = len(func_call) + 1 + stripped_left += item[:length] + stripped_right += ')' + item = item[length:-1].strip() + index += 1 + + # Errors on "all" wildcard star. + if item == '*': + # Wildcard only acceptable in SELECT clauses. + if not allow_wildcard: + raise ValueError('The * identifier can only be used in a SELECT clause.') + # Is SELECT clause. However, should not pass wildcard in addition to other values. + if not found_functions: + raise ValueError('SELECT clause provided * with other params. * is only valid alone.') + + # Validate individual identifier. + if item != '*': + results = self._identifier(item) + if results[0] is False: + raise ValueError('Invalid identifier. Identifier {0}'.format(results[1])) + + # If we made it this far, item is valid. Escape with backticks and readd. + is_quoted = self._is_quoted(item) + if is_quoted: + # Was already quoted, but may not be with expected format. Reformat to guaranteed use expected format. + item = '{1}{0}{1}'.format(item[1:-1], quote_format) + elif item == '*': + pass + else: + # Was not quoted. Add quotes. + item = '{1}{0}{1}'.format(item, quote_format) + + # Re-add function values. + item = stripped_left.upper() + item + stripped_right + + # Append updated value to clause. + new_clause.append(item) + + # All items in clause were valid. Re-concatenate into single expected str format. + clause = ', '.join(new_clause) + + # Return validated and sanitized SELECT clause. + return clause + + # endregion Helper Functions diff --git a/tests/connectors/core/test_validate.py b/tests/connectors/core/test_validate.py index 84e2d15ab68a53409ce46667037a35529b0556f0..2619d9547788f5dea2cec2d305d51c3ecdf28971 100644 --- a/tests/connectors/core/test_validate.py +++ b/tests/connectors/core/test_validate.py @@ -28,7 +28,9 @@ class CoreValidateTestMixin: # Initialize variables. cls.unallowed_char_list = [';', '\\'] cls.unallowed_unicode_index_list = [59, 92] - cls._identifier_str = None + cls._quote_columns_format = None + cls._quote_select_identifier_format = None + cls._quote_str_literal_format = None # region Name Validation @@ -492,13 +494,13 @@ class CoreValidateTestMixin: Tests column validation logic, using str object. """ with self.subTest('Minimal value'): - self.assertEqual( + self.assertText( self.connector.validate.table_columns('id INT'), '( id INT )', ) with self.subTest('Multi-value'): - self.assertEqual( + self.assertText( self.connector.validate.table_columns( 'id INT NOT NULL AUTO_INCREMENT, ' 'title VARCHAR(100) NOT NULL, ' @@ -520,13 +522,13 @@ class CoreValidateTestMixin: Tests column validation logic, using dict object. """ with self.subTest('Minimal value'): - self.assertEqual( + self.assertText( self.connector.validate.table_columns({'id': 'INT'}), '( id INT )', ) with self.subTest('Multi-value'): - self.assertEqual( + self.assertText( self.connector.validate.table_columns({ 'id': 'INT NOT NULL AUTO_INCREMENT', 'title': 'VARCHAR(100) NOT NULL', @@ -701,460 +703,460 @@ class CoreValidateTestMixin: the "standard" database quote types (', ", and `), and then properly converts it to the actual type/format as expected by the given database. """ - if self._identifier_str is None: - TypeError('Invalid _identifier_str variable. Is None.') + if self._quote_select_identifier_format is None: + TypeError('Invalid _select_identifier_clause_format_str variable. Is None.') # None provided. Defaults back to "*". result = self.connector.validate.sanitize_select_identifier_clause(None) - self.assertEqual(result, '*') + self.assertText(result, '*') # All flag provided. result = self.connector.validate.sanitize_select_identifier_clause('*') - self.assertEqual(result, '*') + self.assertText(result, '*') with self.subTest('Values as str - Without quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause('id') - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause(' id ') - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause('id, name') - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name') + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name') ), ) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause(' id , name ') - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause('id, name, code') - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause(' id , name , code ') - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as triple str - Without quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause("""id""") - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause(""" id """) - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause("""id, name""") - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause(""" id , name """) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause("""id, name, code""") - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause(""" id , name , code """) - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as list - Without quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause(['id']) - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause([' id ']) - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause(['id', 'name']) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause([' id ', ' name ']) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause(['id', 'name', 'code']) - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause([' id ', ' name ', ' code ']) - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as tuple - Without quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause(('id',)) - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause((' id ',)) - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause(('id', 'name')) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause((' id ', ' name ')) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause(('id', 'name', 'code')) - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) # With extra whitespace. result = self.connector.validate.sanitize_select_identifier_clause((' id ', ' name ', ' code ')) - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as str - With single quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause("'id'") - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause("'id', 'name'") - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause("'id', 'name', 'code'") - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code') + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code') ), ) with self.subTest('Values as list - With single quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause(["'id'"]) - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause(["'id'", "'name'"]) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause(["'id'", "'name'", "'code'"]) - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as tuple - With single quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause(("'id'",)) - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause(("'id'", "'name'")) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause(("'id'", "'name'", "'code'")) - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as str - With double quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause('"id"') - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause('"id", "name"') - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause('"id", "name", code') - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as list - With double quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause(['"id"']) - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause(['"id"', '"name"']) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause(['"id"', '"name"', '"code"']) - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as tuple - With double quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause(('"id"',)) - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause(('"id"', '"name"')) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause(('"id"', '"name"', '"code"')) - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as str - With backtick quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause('`id`') - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause('`id`, `name`') - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause('`id`, `name`, `code`') - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as list - With backtick quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause(['`id`']) - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause(['`id`', '`name`']) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause(['`id`', '`name`', '`code`']) - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as tuple - With backtick quotes'): # Single val provided. result = self.connector.validate.sanitize_select_identifier_clause(('`id`',)) - self.assertEqual(result, self._identifier_str.format('id')) + self.assertText(result, self._quote_select_identifier_format.format('id')) # Two vals provided. result = self.connector.validate.sanitize_select_identifier_clause(('`id`', '`name`')) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), ), ) # Three vals provided. result = self.connector.validate.sanitize_select_identifier_clause(('`id`', '`name`', '`code`')) - self.assertEqual( + self.assertText( result, '{0}, {1}, {2}'.format( - self._identifier_str.format('id'), - self._identifier_str.format('name'), - self._identifier_str.format('code'), + self._quote_select_identifier_format.format('id'), + self._quote_select_identifier_format.format('name'), + self._quote_select_identifier_format.format('code'), ), ) with self.subTest('Values as non-standard types'): result = self.connector.validate.sanitize_select_identifier_clause((1, True)) - self.assertEqual( + self.assertText( result, '{0}, {1}'.format( - self._identifier_str.format(1), - self._identifier_str.format(True), + self._quote_select_identifier_format.format(1), + self._quote_select_identifier_format.format(True), ), ) with self.subTest('Values with function calls'): # Uppercase. result = self.connector.validate.sanitize_select_identifier_clause('COUNT(*)') - self.assertEqual(result, 'COUNT(*)') + self.assertText(result, 'COUNT(*)') # Lowercase. result = self.connector.validate.sanitize_select_identifier_clause('count(*)') - self.assertEqual(result, 'COUNT(*)') + self.assertText(result, 'COUNT(*)') def test__sanitize_select_identifier_clause__failure(self): """ @@ -1163,14 +1165,14 @@ class CoreValidateTestMixin: # Param "*" provided with other values. with self.assertRaises(ValueError) as err: self.connector.validate.sanitize_select_identifier_clause('* , id') - self.assertEqual('SELECT clause provided * with other params. * is only valid alone.', str(err.exception)) + self.assertText('SELECT clause provided * with other params. * is only valid alone.', str(err.exception)) # Mistmatching quotes - double then single. identifier = """\"id'""" with self.assertRaises(ValueError) as err: self.connector.validate.sanitize_select_identifier_clause(identifier) - self.assertEqual( - 'Invalid SELECT identifier. Identifier does not match acceptable characters.\n Identifier is: "id\'', + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: "id\'', str(err.exception), ) @@ -1178,8 +1180,8 @@ class CoreValidateTestMixin: identifier = """'id\"""" with self.assertRaises(ValueError) as err: self.connector.validate.sanitize_select_identifier_clause(identifier) - self.assertEqual( - 'Invalid SELECT identifier. Identifier does not match acceptable characters.\n Identifier is: \'id"', + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: \'id"', str(err.exception), ) @@ -1187,8 +1189,8 @@ class CoreValidateTestMixin: identifier = "`id'" with self.assertRaises(ValueError) as err: self.connector.validate.sanitize_select_identifier_clause(identifier) - self.assertEqual( - 'Invalid SELECT identifier. Identifier does not match acceptable characters.\n Identifier is: `id\'', + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: `id\'', str(err.exception), ) @@ -1196,8 +1198,8 @@ class CoreValidateTestMixin: identifier = "'id`" with self.assertRaises(ValueError) as err: self.connector.validate.sanitize_select_identifier_clause(identifier) - self.assertEqual( - 'Invalid SELECT identifier. Identifier does not match acceptable characters.\n Identifier is: \'id`', + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: \'id`', str(err.exception), ) @@ -1205,8 +1207,8 @@ class CoreValidateTestMixin: identifier = '"id`' with self.assertRaises(ValueError) as err: self.connector.validate.sanitize_select_identifier_clause(identifier) - self.assertEqual( - 'Invalid SELECT identifier. Identifier does not match acceptable characters.\n Identifier is: "id`', + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: "id`', str(err.exception), ) @@ -1214,11 +1216,2532 @@ class CoreValidateTestMixin: identifier = '`id"' with self.assertRaises(ValueError) as err: self.connector.validate.sanitize_select_identifier_clause(identifier) - self.assertEqual( - 'Invalid SELECT identifier. Identifier does not match acceptable characters.\n Identifier is: `id"', + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: `id"', str(err.exception), ) + def test__sanitize_columns_clause__success(self): + """ + Test sanitizing a COLUMNS clause, in cases when it should succeed. + + For the most part, we test that the library gracefully handles any of + the "standard" database quote types (', ", and `), and then properly + converts it to the actual type/format as expected by the given database. + """ + if self._quote_columns_format is None: + TypeError('Invalid _columns_clause_format_str variable. Is None.') + + # None provided. Defaults back to empty string. + result = self.connector.validate.sanitize_columns_clause(None) + self.assertText(result, '') + + # Empty string provided (single-quote str). + result = self.connector.validate.sanitize_columns_clause('') + self.assertText(result, '') + + # Empty string provided (double-quote str). + result = self.connector.validate.sanitize_columns_clause("") + self.assertText(result, '') + + # Empty string provided (triple double-quote str). + result = self.connector.validate.sanitize_columns_clause("""""") + self.assertText(result, '') + + with self.subTest('Values as str - Without quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause('id') + self.assertText(result, self._quote_columns_format.format('id')) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause(' id ') + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause('id, name') + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name') + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause(' id , name ') + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause('id, name, code') + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause(' id , name , code ') + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as triple str - Without quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause("""id""") + self.assertText(result, self._quote_columns_format.format('id')) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause(""" id """) + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause("""id, name""") + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause(""" id , name """) + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause("""id, name, code""") + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause(""" id , name , code """) + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as list - Without quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause(['id']) + self.assertText(result, self._quote_columns_format.format('id')) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause([' id ']) + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause(['id', 'name']) + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause([' id ', ' name ']) + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause(['id', 'name', 'code']) + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause([' id ', ' name ', ' code ']) + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as tuple - Without quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause(('id',)) + self.assertText(result, self._quote_columns_format.format('id')) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause((' id ',)) + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause(('id', 'name')) + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause((' id ', ' name ')) + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause(('id', 'name', 'code')) + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_columns_clause((' id ', ' name ', ' code ')) + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as str - With single quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause("'id'") + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause("'id', 'name'") + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause("'id', 'name', 'code'") + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code') + ), + ) + + with self.subTest('Values as list - With single quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause(["'id'"]) + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause(["'id'", "'name'"]) + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause(["'id'", "'name'", "'code'"]) + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as tuple - With single quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause(("'id'",)) + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause(("'id'", "'name'")) + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause(("'id'", "'name'", "'code'")) + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as str - With double quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause('"id"') + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause('"id", "name"') + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause('"id", "name", code') + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as list - With double quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause(['"id"']) + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause(['"id"', '"name"']) + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause(['"id"', '"name"', '"code"']) + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as tuple - With double quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause(('"id"',)) + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause(('"id"', '"name"')) + self.assertText( + result, '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause(('"id"', '"name"', '"code"')) + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as str - With backtick quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause('`id`') + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause('`id`, `name`') + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause('`id`, `name`, `code`') + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as list - With backtick quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause(['`id`']) + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause(['`id`', '`name`']) + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause(['`id`', '`name`', '`code`']) + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as tuple - With backtick quotes'): + # Single val provided. + result = self.connector.validate.sanitize_columns_clause(('`id`',)) + self.assertText(result, self._quote_columns_format.format('id')) + + # Two vals provided. + result = self.connector.validate.sanitize_columns_clause(('`id`', '`name`')) + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_columns_clause(('`id`', '`name`', '`code`')) + self.assertText( + result, + '{0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as non-standard types'): + # TODO: Should these fail? These probably should fail. + # I think only literal column names should work. + result = self.connector.validate.sanitize_columns_clause((1, True)) + self.assertText( + result, + '{0}, {1}'.format( + self._quote_columns_format.format(1), + self._quote_columns_format.format(True), + ), + ) + + def test__sanitize_columns_clause__failure(self): + """ + Test sanitizing a COLUMNS clause, in cases when it should fail. + """ + # Param "*" provided. + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_columns_clause('*') + self.assertText('The * identifier can only be used in a SELECT clause.', str(err.exception)) + + # Param "*" provided with other values. + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_columns_clause('* , id') + self.assertText('The * identifier can only be used in a SELECT clause.', str(err.exception)) + + # Mistmatching quotes - double then single. + identifier = """\"id'""" + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_columns_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: "id\'', + str(err.exception), + ) + + # Mistmatching quotes - single then double. + identifier = """'id\"""" + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_columns_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: \'id"', + str(err.exception), + ) + + # Mistmatching quotes - backtick then single. + identifier = "`id'" + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_columns_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: `id\'', + str(err.exception), + ) + + # Mistmatching quotes - single then backtick. + identifier = "'id`" + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_columns_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: \'id`', + str(err.exception), + ) + + # Mistmatching quotes - double then backtick. + identifier = '"id`' + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_columns_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: "id`', + str(err.exception), + ) + + # Mistmatching quotes - backtick then double. + identifier = '`id"' + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_columns_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: `id"', + str(err.exception), + ) + + # def test__sanitize_values_clause__success(self): + # """ + # Test sanitizing a VALUES clause, in cases when it should succeed. + # + # For the most part, we test that the library gracefully handles any of + # the "standard" database quote types (', ", and `), and then properly + # converts it to the actual type/format as expected by the given database. + # """ + # if self._quote_columns_format is None: + # TypeError('Invalid _columns_clause_format_str variable. Is None.') + # + # with self.subTest('Values as str - Without quotes'): + # + # with self.subTest('Single val provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause('id') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(' id ') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause('VALUES id') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause('values id') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause('VALUES (id)') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause('values (id)') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # with self.subTest('Two vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause('id, name') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(' id , name ') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause('VALUES id, name') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause('values id, name') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause('VALUES (id, name)') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause('values (id, name)') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # with self.subTest('Three vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause('id, name, code') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(' id , name , code ') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause('VALUES id, name, code') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause('values id, name, code') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause('VALUES (id, name, code)') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause('values (id, name, code)') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # with self.subTest('Values as triple str - Without quotes'): + # + # with self.subTest('Single val provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause("""id""") + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(""" id """) + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause("""VALUES id""") + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause("""values id""") + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause("""VALUES (id)""") + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause("""values (id)""") + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # with self.subTest('Two vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause("""id, name""") + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(""" id , name """) + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause("""VALUES id, name""") + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause("""values id, name""") + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause("""VALUES (id, name)""") + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause("""values (id, name)""") + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # with self.subTest('Three vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause("""id, name, code""") + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(""" id , name , code """) + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause("""VALUES id, name, code""") + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause("""values id, name, code""") + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause("""VALUES (id, name, code)""") + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause("""values (id, name, code)""") + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # with self.subTest('Values as list - Without quotes'): + # + # with self.subTest('Single val provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(['id']) + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause([' id ']) + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format(' id '))) + # + # with self.subTest('Two vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(['id', 'name']) + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause([' id ', ' name ']) + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format(' id '), + # self._quote_str_literal_format.format(' name '), + # ), + # ) + # + # with self.subTest('Three vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(['id', 'name', 'code']) + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause([' id ', ' name ', ' code ']) + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format(' id '), + # self._quote_str_literal_format.format(' name '), + # self._quote_str_literal_format.format(' code '), + # ), + # ) + # + # with self.subTest('Values as tuple - Without quotes'): + # + # with self.subTest('Single val provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(('id',)) + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause((' id ',)) + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format(' id '))) + # + # with self.subTest('Two vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(('id', 'name')) + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause((' id ', ' name ')) + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format(' id '), + # self._quote_str_literal_format.format(' name '), + # ), + # ) + # + # with self.subTest('Three vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(('id', 'name', 'code')) + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause((' id ', ' name ', ' code ')) + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format(' id '), + # self._quote_str_literal_format.format(' name '), + # self._quote_str_literal_format.format(' code '), + # ), + # ) + # + # with self.subTest('Values as str - With single quotes'): + # + # with self.subTest('Single val provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause("'id'") + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(" ' id ' ") + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format(' id '))) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause("VALUES 'id'") + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause("values 'id'") + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause("VALUES ('id')") + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause("values ('id')") + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # with self.subTest('Two vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause("'id', 'name'") + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(" ' id ' , ' name ' ") + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format(' id '), + # self._quote_str_literal_format.format(' name '), + # ), + # ) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause("VALUES 'id', 'name',") + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause("values 'id', 'name'") + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause("VALUES ('id', 'name')") + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause("values ('id', 'name')") + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # with self.subTest('Three vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause("'id', 'name', 'code'") + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(" ' id ' , ' name ' , ' code ' ") + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format(' id '), + # self._quote_str_literal_format.format(' name '), + # self._quote_str_literal_format.format(' code '), + # ), + # ) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause("VALUES 'id', 'name', 'code'") + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause("values 'id', 'name', 'code'") + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause("VALUES ('id', 'name', 'code')") + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause("values ('id', 'name', 'code')") + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # with self.subTest('Values as list - With single quotes'): + # # + # # with self.subTest('Single val provided'): + # # # Base value. + # # print('\n\n\n\n\n\n\n\n\n\n\n\n\n') + # # # All of these tokenize to the same thing? Okay then... + # # # TODO: Figure out how to tokenize this as expected. + # # result = self.connector.validate.sanitize_values_clause('id') + # # result = self.connector.validate.sanitize_values_clause("id") + # # result = self.connector.validate.sanitize_values_clause("'id'") + # # result = self.connector.validate.sanitize_values_clause(["'id'"]) + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format("'id'"))) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause([" ' id ' "]) + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format(" ' id ' "))) + # # + # # with self.subTest('Two vals provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause(["'id'", "'name'"]) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format("'id'"), + # # self._quote_str_literal_format.format("'name'"), + # # ), + # # ) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause([" ' id ' ", " ' name ' "]) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format(" ' id ' "), + # # self._quote_str_literal_format.format(" ' name ' "), + # # ), + # # ) + # # + # # with self.subTest('Three vals provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause(["'id'", "'name'", "'code'"]) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format("'id'"), + # # self._quote_str_literal_format.format("'name'"), + # # self._quote_str_literal_format.format("'code'"), + # # ), + # # ) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause([" ' id ' ", " ' name ' ", " ' code ' "]) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format(" ' id ' "), + # # self._quote_str_literal_format.format(" ' name ' "), + # # self._quote_str_literal_format.format(" ' code ' "), + # # ), + # # ) + # + # # with self.subTest('Values as tuple - With single quotes'): + # # + # # with self.subTest('Single val provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause(("'id'",)) + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format("'id'"))) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause((" ' id ' ",)) + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format(" ' id ' "))) + # # + # # with self.subTest('Two vals provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause(("'id'", "'name'")) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format("'id'"), + # # self._quote_str_literal_format.format("'name'"), + # # ), + # # ) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause((" ' id ' ", " ' name ' ")) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format(" ' id ' "), + # # self._quote_str_literal_format.format(" ' name ' "), + # # ), + # # ) + # # + # # with self.subTest('Three vals provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause(("'id'", "'name'", "'code'")) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format("'id'"), + # # self._quote_str_literal_format.format("'name'"), + # # self._quote_str_literal_format.format("'code'"), + # # ), + # # ) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause((" ' id ' ", " ' name ' ", " ' code ' ")) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format(" ' id ' "), + # # self._quote_str_literal_format.format(" ' name ' "), + # # self._quote_str_literal_format.format(" ' code ' "), + # # ), + # # ) + # + # with self.subTest('Values as str - With double quotes'): + # + # with self.subTest('Single val provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause('"id"') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(' " id " ') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format(' id '))) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause('VALUES "id"') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause('values "id"') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause('VALUES ("id")') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause('values ("id")') + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('id'))) + # + # with self.subTest('Two vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause('"id", "name"') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(' " id " , " name " ') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format(' id '), + # self._quote_str_literal_format.format(' name '), + # ), + # ) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause('VALUES "id", "name"') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause('values "id", "name"') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause('VALUES ("id", "name")') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause('values ("id", "name")') + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # ), + # ) + # + # with self.subTest('Three vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause('"id", "name", "code"') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause(' " id " , " name " , " code " ') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format(' id '), + # self._quote_str_literal_format.format(' name '), + # self._quote_str_literal_format.format(' code '), + # ), + # ) + # + # # With full statement - upper, no parens. + # result = self.connector.validate.sanitize_values_clause('VALUES "id", "name", "code"') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - lower, no parens. + # result = self.connector.validate.sanitize_values_clause('values "id", "name", "code"') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - upper, with parens. + # result = self.connector.validate.sanitize_values_clause('VALUES ("id", "name", "code")') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # # With full statement - lower, with parens. + # result = self.connector.validate.sanitize_values_clause('values ("id", "name", "code")') + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('id'), + # self._quote_str_literal_format.format('name'), + # self._quote_str_literal_format.format('code'), + # ), + # ) + # + # with self.subTest('Values as list - With double quotes'): + # with self.subTest('Single val provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(['"id"']) + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('"id"'))) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause([' " id " ']) + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format(' " id " '))) + # + # with self.subTest('Two vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(['"id"', '"name"']) + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('"id"'), + # self._quote_str_literal_format.format('"name"'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause([' " id " ', ' " name " ']) + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format(' " id " '), + # self._quote_str_literal_format.format(' " name " '), + # ), + # ) + # + # with self.subTest('Three vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(['"id"', '"name"', '"code"']) + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('"id"'), + # self._quote_str_literal_format.format('"name"'), + # self._quote_str_literal_format.format('"code"'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause([' " id " ', ' " name " ', ' " code " ']) + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format(' " id " '), + # self._quote_str_literal_format.format(' " name " '), + # self._quote_str_literal_format.format(' " code " '), + # ), + # ) + # + # with self.subTest('Values as tuple - With double quotes'): + # + # with self.subTest('Single val provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(('"id"',)) + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('"id"'))) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause((' " id " ',)) + # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format(' " id " '))) + # + # with self.subTest('Two vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(('"id"', '"name"')) + # self.assertText( + # result, '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format('"id"'), + # self._quote_str_literal_format.format('"name"'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause((' " id " ', ' " name " ')) + # self.assertText( + # result, '\nVALUES ({0}, {1})'.format( + # self._quote_str_literal_format.format(' " id " '), + # self._quote_str_literal_format.format(' " name " '), + # ), + # ) + # + # with self.subTest('Three vals provided'): + # # Base value. + # result = self.connector.validate.sanitize_values_clause(('"id"', '"name"', '"code"')) + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format('"id"'), + # self._quote_str_literal_format.format('"name"'), + # self._quote_str_literal_format.format('"code"'), + # ), + # ) + # + # # With extra whitespace. + # result = self.connector.validate.sanitize_values_clause((' " id " ', ' " name " ', ' " code " ')) + # self.assertText( + # result, + # '\nVALUES ({0}, {1}, {2})'.format( + # self._quote_str_literal_format.format(' " id " '), + # self._quote_str_literal_format.format(' " name " '), + # self._quote_str_literal_format.format(' " code " '), + # ), + # ) + # + # # with self.subTest('Values as str - With backtick quotes'): + # # + # # with self.subTest('Single val provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause('`id`') + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('`id`'))) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause(' ` id ` ') + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('` id `'))) + # # + # # # With full statement - upper, no parens. + # # result = self.connector.validate.sanitize_values_clause('VALUES `id`') + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('`id`'))) + # # + # # # With full statement - lower, no parens. + # # result = self.connector.validate.sanitize_values_clause('values `id`') + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('`id`'))) + # # + # # # With full statement - upper, no parens. + # # result = self.connector.validate.sanitize_values_clause('VALUES `id`') + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('`id`'))) + # # + # # # With full statement - lower, no parens. + # # result = self.connector.validate.sanitize_values_clause('values `id`') + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('`id`'))) + # # + # # with self.subTest('Two vals provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause('`id`, `name`') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # ), + # # ) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause(' ` id ` , ` name ` ') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format('` id `'), + # # self._quote_str_literal_format.format('` name `'), + # # ), + # # ) + # # + # # # With full statement - upper, no parens. + # # result = self.connector.validate.sanitize_values_clause('VALUES `id`, `name`') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # ), + # # ) + # # + # # # With full statement - lower, no parens. + # # result = self.connector.validate.sanitize_values_clause('values `id`, `name`') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # ), + # # ) + # # + # # # With full statement - upper, no parens. + # # result = self.connector.validate.sanitize_values_clause('VALUES `id`, `name`') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # ), + # # ) + # # + # # # With full statement - lower, no parens. + # # result = self.connector.validate.sanitize_values_clause('values `id`, `name`') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # ), + # # ) + # # + # # with self.subTest('Three vals provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause('`id`, `name`, `code`') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # self._quote_str_literal_format.format('`code`'), + # # ), + # # ) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause(' ` id ` , ` name ` , ` code ` ') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # self._quote_str_literal_format.format('`code`'), + # # ), + # # ) + # # + # # # With full statement - upper, no parens. + # # result = self.connector.validate.sanitize_values_clause('VALUES `id`, `name`, `code`') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # self._quote_str_literal_format.format('`code`'), + # # ), + # # ) + # # + # # # With full statement - lower, no parens. + # # result = self.connector.validate.sanitize_values_clause('values `id`, `name`, `code`') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # self._quote_str_literal_format.format('`code`'), + # # ), + # # ) + # # + # # # With full statement - upper, no parens. + # # result = self.connector.validate.sanitize_values_clause('VALUES `id`, `name`, `code`') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # self._quote_str_literal_format.format('`code`'), + # # ), + # # ) + # # + # # # With full statement - lower, no parens. + # # result = self.connector.validate.sanitize_values_clause('values `id`, `name`, `code`') + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # self._quote_str_literal_format.format('`code`'), + # # ), + # # ) + # + # # with self.subTest('Values as list - With backtick quotes'): + # # + # # with self.subTest('Single val provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause(['`id`']) + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('`id`'))) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause([' ` id ` ']) + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('` id `'))) + # # + # # with self.subTest('Two vals provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause(['`id`', '`name`']) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # ), + # # ) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause([' ` id ` ', ' ` name ` ']) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format(' ` id ` '), + # # self._quote_str_literal_format.format(' ` name ` '), + # # ), + # # ) + # # + # # with self.subTest('Three vals provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause(['`id`', '`name`', '`code`']) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # self._quote_str_literal_format.format('`code`'), + # # ), + # # ) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause([' ` id ` ', ' ` name ` ', ' ` code ` ']) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format(' ` id ` '), + # # self._quote_str_literal_format.format(' ` name ` '), + # # self._quote_str_literal_format.format(' ` code ` '), + # # ), + # # ) + # + # # with self.subTest('Values as tuple - With backtick quotes'): + # # + # # with self.subTest('Single val provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause(('`id`',)) + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format('`id`'))) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause((' ` id ` ',)) + # # self.assertText(result, '\nVALUES ({0})'.format(self._quote_str_literal_format.format(' ` id ` '))) + # # + # # with self.subTest('Two vals provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause(('`id`', '`name`')) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # ), + # # ) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause((' ` id ` ', ' ` name ` ')) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1})'.format( + # # self._quote_str_literal_format.format(' ` id ` '), + # # self._quote_str_literal_format.format(' ` name ` '), + # # ), + # # ) + # # + # # with self.subTest('Three vals provided'): + # # # Base value. + # # result = self.connector.validate.sanitize_values_clause(('`id`', '`name`', '`code`')) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format('`id`'), + # # self._quote_str_literal_format.format('`name`'), + # # self._quote_str_literal_format.format('`code`'), + # # ), + # # ) + # # + # # # With extra whitespace. + # # result = self.connector.validate.sanitize_values_clause((' ` id ` ', ' ` name ` ', ' ` code ` ')) + # # self.assertText( + # # result, + # # '\nVALUES ({0}, {1}, {2})'.format( + # # self._quote_str_literal_format.format(' ` id ` '), + # # self._quote_str_literal_format.format(' ` name ` '), + # # self._quote_str_literal_format.format(' ` code ` '), + # # ), + # # ) + # + # with self.subTest('Values as non-standard types'): + # result = self.connector.validate.sanitize_values_clause((1, True)) + # self.assertText( + # result, + # '\nVALUES ({0}, {1})'.format( + # 1, + # True, + # ), + # ) + # + # # # Mistmatching quotes - double then single. + # # identifier = """\"id'""" + # # result = self.connector.validate.sanitize_values_clause(identifier) + # # self.assertText( + # # 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: "id\'', + # # result, + # # ) + # # + # # # Mistmatching quotes - single then double. + # # identifier = """'id\"""" + # # result = self.connector.validate.sanitize_values_clause(identifier) + # # self.assertText( + # # 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: \'id"', + # # result, + # # ) + # # + # # # Mistmatching quotes - backtick then single. + # # identifier = "`id'" + # # result = self.connector.validate.sanitize_values_clause(identifier) + # # self.assertText( + # # 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: `id\'', + # # result, + # # ) + # # + # # # Mistmatching quotes - single then backtick. + # # identifier = "'id`" + # # result = self.connector.validate.sanitize_values_clause(identifier) + # # self.assertText( + # # 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: \'id`', + # # result, + # # ) + # # + # # # Mistmatching quotes - double then backtick. + # # identifier = '"id`' + # # result = self.connector.validate.sanitize_values_clause(identifier) + # # self.assertText( + # # 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: "id`', + # # result, + # # ) + # # + # # # Mistmatching quotes - backtick then double. + # # identifier = '`id"' + # # result = self.connector.validate.sanitize_values_clause(identifier) + # # self.assertText( + # # 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: `id"', + # # result, + # # ) + + # def test__sanitize_values_clause__failure(self): + # """ + # Test sanitizing a VALUES clause, in cases when it should fail. + # """ + # # None provided. + # with self.assertRaises(ValueError) as err: + # self.connector.validate.sanitize_values_clause(None) + # self.assertText('Invalid VALUES clause. Must have one or more items.', str(err.exception)) + # + # # Empty string provided (single-quote str). + # with self.assertRaises(ValueError) as err: + # self.connector.validate.sanitize_values_clause('') + # self.assertText('Invalid VALUES clause. Must have one or more items.', str(err.exception)) + # + # # Empty string provided (double-quote str). + # with self.assertRaises(ValueError) as err: + # self.connector.validate.sanitize_values_clause("") + # self.assertText('Invalid VALUES clause. Must have one or more items.', str(err.exception)) + # + # # Empty string provided (triple double-quote str). + # with self.assertRaises(ValueError) as err: + # self.connector.validate.sanitize_values_clause("""""") + # self.assertText('Invalid VALUES clause. Must have one or more items.', str(err.exception)) + # + # # "VALUES" provided without any additional values. + # with self.assertRaises(ValueError) as err: + # self.connector.validate.sanitize_values_clause('VALUES') + # self.assertText('Invalid VALUES clause. Must have one or more items.', str(err.exception)) + # with self.assertRaises(ValueError) as err: + # self.connector.validate.sanitize_values_clause(' VALUES ') + # self.assertText('Invalid VALUES clause. Must have one or more items.', str(err.exception)) + # + # # Param "*" provided. + # with self.assertRaises(ValueError) as err: + # self.connector.validate.sanitize_values_clause('*') + # self.assertText('The * identifier can only be used in a SELECT clause.', str(err.exception)) + # + # # Param "*" provided with other values. + # with self.assertRaises(ValueError) as err: + # self.connector.validate.sanitize_values_clause('* , id') + # self.assertText('The * identifier can only be used in a SELECT clause.', str(err.exception)) + + def test__sanitize_order_by_clause__success(self): + """ + Test sanitizing an ORDER BY clause, in cases when it should succeed. + + For the most part, we test that the library gracefully handles any of + the "standard" database quote types (', ", and `), and then properly + converts it to the actual type/format as expected by the given database. + """ + if self._quote_columns_format is None: + TypeError('Invalid _columns_clause_format_str variable. Is None.') + + # None provided. Defaults back to empty string. + result = self.connector.validate.sanitize_order_by_clause(None) + self.assertText(result, '') + + # Empty string provided (single-quote str). + result = self.connector.validate.sanitize_order_by_clause('') + self.assertText(result, '') + + # Empty string provided (double-quote str). + result = self.connector.validate.sanitize_order_by_clause("") + self.assertText(result, '') + + # Empty string provided (triple double-quote str). + result = self.connector.validate.sanitize_order_by_clause("""""") + self.assertText(result, '') + + with self.subTest('Values as str - Without quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause('id') + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause(' id ') + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With full statement - upper. + result = self.connector.validate.sanitize_order_by_clause('ORDER BY id') + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With full statement - lower. + result = self.connector.validate.sanitize_order_by_clause('order by id') + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause('id, name') + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name') + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause(' id , name ') + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause('id, name, code') + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause(' id , name , code ') + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as triple str - Without quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause("""id""") + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause(""" id """) + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With full statement - upper. + result = self.connector.validate.sanitize_order_by_clause("""ORDER BY id""") + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With full statement - lower. + result = self.connector.validate.sanitize_order_by_clause("""order by id""") + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause("""id, name""") + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause(""" id , name """) + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause("""id, name, code""") + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause(""" id , name , code """) + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as list - Without quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause(['id']) + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause([' id ']) + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause(['id', 'name']) + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause([' id ', ' name ']) + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause(['id', 'name', 'code']) + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause([' id ', ' name ', ' code ']) + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as tuple - Without quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause(('id',)) + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause((' id ',)) + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause(('id', 'name')) + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause((' id ', ' name ')) + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause(('id', 'name', 'code')) + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + # With extra whitespace. + result = self.connector.validate.sanitize_order_by_clause((' id ', ' name ', ' code ')) + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as str - With single quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause("'id'") + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With full statement - upper. + result = self.connector.validate.sanitize_order_by_clause("ORDER BY 'id'") + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With full statement - lower. + result = self.connector.validate.sanitize_order_by_clause("order by 'id'") + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause("'id', 'name'") + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause("'id', 'name', 'code'") + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code') + ), + ) + + with self.subTest('Values as list - With single quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause(["'id'"]) + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause(["'id'", "'name'"]) + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause(["'id'", "'name'", "'code'"]) + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as tuple - With single quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause(("'id'",)) + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause(("'id'", "'name'")) + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause(("'id'", "'name'", "'code'")) + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as str - With double quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause('"id"') + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With full statement - upper. + result = self.connector.validate.sanitize_order_by_clause('ORDER BY "id"') + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With full statement - lower. + result = self.connector.validate.sanitize_order_by_clause('order by "id"') + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause('"id", "name"') + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause('"id", "name", code') + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as list - With double quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause(['"id"']) + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause(['"id"', '"name"']) + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause(['"id"', '"name"', '"code"']) + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as tuple - With double quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause(('"id"',)) + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause(('"id"', '"name"')) + self.assertText( + result, '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause(('"id"', '"name"', '"code"')) + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as str - With backtick quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause('`id`') + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With full statement - upper. + result = self.connector.validate.sanitize_order_by_clause('ORDER BY `id`') + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + # With full statement - lower. + result = self.connector.validate.sanitize_order_by_clause('order by `id`') + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause('`id`, `name`') + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause('`id`, `name`, `code`') + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as list - With backtick quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause(['`id`']) + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause(['`id`', '`name`']) + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause(['`id`', '`name`', '`code`']) + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as tuple - With backtick quotes'): + # Single val provided. + result = self.connector.validate.sanitize_order_by_clause(('`id`',)) + self.assertText(result, '\nORDER BY {0}'.format(self._quote_columns_format.format('id'))) + + # Two vals provided. + result = self.connector.validate.sanitize_order_by_clause(('`id`', '`name`')) + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + ), + ) + + # Three vals provided. + result = self.connector.validate.sanitize_order_by_clause(('`id`', '`name`', '`code`')) + self.assertText( + result, + '\nORDER BY {0}, {1}, {2}'.format( + self._quote_columns_format.format('id'), + self._quote_columns_format.format('name'), + self._quote_columns_format.format('code'), + ), + ) + + with self.subTest('Values as non-standard types'): + # TODO: Should these fail? These probably should fail. + # I think only literal column names should work. + result = self.connector.validate.sanitize_order_by_clause((1, True)) + self.assertText( + result, + '\nORDER BY {0}, {1}'.format( + self._quote_columns_format.format(1), + self._quote_columns_format.format(True), + ), + ) + + def test__sanitize_order_by_clause__failure(self): + """ + Test sanitizing an ORDER BY clause, in cases when it should fail. + """ + # "ORDER BY" provided without any additional values. + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_order_by_clause('ORDER BY') + self.assertText('Invalid ORDER BY clause.', str(err.exception)) + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_order_by_clause(' ORDER BY ') + self.assertText('Invalid ORDER BY clause.', str(err.exception)) + + # Param "*" provided. + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_order_by_clause('*') + self.assertText('The * identifier can only be used in a SELECT clause.', str(err.exception)) + + # Param "*" provided with other values. + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_order_by_clause('* , id') + self.assertText('The * identifier can only be used in a SELECT clause.', str(err.exception)) + + # Mistmatching quotes - double then single. + identifier = """\"id'""" + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_order_by_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: "id\'', + str(err.exception), + ) + + # Mistmatching quotes - single then double. + identifier = """'id\"""" + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_order_by_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: \'id"', + str(err.exception), + ) + + # Mistmatching quotes - backtick then single. + identifier = "`id'" + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_order_by_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: `id\'', + str(err.exception), + ) + + # Mistmatching quotes - single then backtick. + identifier = "'id`" + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_order_by_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: \'id`', + str(err.exception), + ) + + # Mistmatching quotes - double then backtick. + identifier = '"id`' + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_order_by_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: "id`', + str(err.exception), + ) + + # Mistmatching quotes - backtick then double. + identifier = '`id"' + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_order_by_clause(identifier) + self.assertText( + 'Invalid identifier. Identifier does not match acceptable characters.\n Identifier is: `id"', + str(err.exception), + ) + + def test__sanitize_limit_clause__success(self): + """ + Test sanitizing a LIMIT clause, in cases when it should succeed. + """ + # None provided. Defaults back to empty string. + result = self.connector.validate.sanitize_limit_clause(None) + self.assertText(result, '') + + # Empty string provided (single-quote str). + result = self.connector.validate.sanitize_limit_clause('') + self.assertText(result, '') + + # Empty string provided (double-quote str). + result = self.connector.validate.sanitize_limit_clause("") + self.assertText(result, '') + + # Empty string provided (triple double-quote str). + result = self.connector.validate.sanitize_limit_clause("""""") + self.assertText(result, '') + + with self.subTest('Limit of 1 (lowest acceptable limit)'): + # As int. + result = self.connector.validate.sanitize_limit_clause(1) + self.assertText(result, ' LIMIT 1') + + # As str. + result = self.connector.validate.sanitize_limit_clause('1') + self.assertText(result, ' LIMIT 1') + + # As full str (upper). + result = self.connector.validate.sanitize_limit_clause('LIMIT 1') + self.assertText(result, ' LIMIT 1') + + # As full str (lower). + result = self.connector.validate.sanitize_limit_clause('limit 1') + self.assertText(result, ' LIMIT 1') + + with self.subTest('Limit of 2'): + # As int. + result = self.connector.validate.sanitize_limit_clause(2) + self.assertText(result, ' LIMIT 2') + + # As str. + result = self.connector.validate.sanitize_limit_clause('2') + self.assertText(result, ' LIMIT 2') + + # As full str (upper). + result = self.connector.validate.sanitize_limit_clause('LIMIT 2') + self.assertText(result, ' LIMIT 2') + + # As full str (lower). + result = self.connector.validate.sanitize_limit_clause('limit 2') + self.assertText(result, ' LIMIT 2') + + with self.subTest('Limit of 100'): + # As int. + result = self.connector.validate.sanitize_limit_clause(100) + self.assertText(result, ' LIMIT 100') + + # As str. + result = self.connector.validate.sanitize_limit_clause('100') + self.assertText(result, ' LIMIT 100') + + # As full str (upper). + result = self.connector.validate.sanitize_limit_clause('LIMIT 100') + self.assertText(result, ' LIMIT 100') + + # As full str (lower). + result = self.connector.validate.sanitize_limit_clause('limit 100') + self.assertText(result, ' LIMIT 100') + + def test__sanitize_limit_clause__failure(self): + """ + Test sanitizing a LIMIT clause, in cases when it should fail. + """ + # Zero provided. + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_limit_clause(0) + self.assertText('The LIMIT clause must return at least one record.', str(err.exception)) + + # Negative provided. + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_limit_clause(-1) + self.assertText('The LIMIT clause must return at least one record.', str(err.exception)) + + # Non-integer. + with self.assertRaises(ValueError) as err: + self.connector.validate.sanitize_limit_clause('abc') + self.assertText('The LIMIT clause expects a positive integer.', str(err.exception)) + # endregion Clause Validation # region Helper Functions diff --git a/tests/connectors/mysql/test_validate.py b/tests/connectors/mysql/test_validate.py index b14e3d4f6125f58fcbead9924946dfa8d1de0a84..cfc7259ae9d0865841e6baf58061fd3fd5ce6ad1 100644 --- a/tests/connectors/mysql/test_validate.py +++ b/tests/connectors/mysql/test_validate.py @@ -35,17 +35,23 @@ class TestMysqlValidate(TestMysqlDatabaseParent, CoreValidateTestMixin): cls.connector.tables.drop(result) # Import here to prevent errors if database type is not installed on system. - from py_dbcn.connectors.mysql.validate import QUOTE_IDENTIFIER_FORMAT + from py_dbcn.connectors.mysql.validate import ( + QUOTE_COLUMN_FORMAT, + QUOTE_IDENTIFIER_FORMAT, + QUOTE_STR_LITERAL_FORMAT, + ) # Initialize variables. - cls._identifier_str = '{0}{1}{0}'.format(QUOTE_IDENTIFIER_FORMAT, '{0}') + cls._quote_columns_format = '{0}{1}{0}'.format(QUOTE_COLUMN_FORMAT, '{0}') + cls._quote_select_identifier_format = '{0}{1}{0}'.format(QUOTE_IDENTIFIER_FORMAT, '{0}') + cls._quote_str_literal_format = '{0}{1}{0}'.format(QUOTE_STR_LITERAL_FORMAT, '{0}') def test__sanitize_select_identifier_clause__success(self): """ Test sanitizing a SELECT clause, in cases when it should succeed. """ # Verify identifier str is as we expect. - self.assertText('`{0}`', self._identifier_str) + self.assertText('`{0}`', self._quote_select_identifier_format) # Call parent logic. super().test__sanitize_select_identifier_clause__success() diff --git a/tests/connectors/postgresql/test_validate.py b/tests/connectors/postgresql/test_validate.py index 977d23d20cd3e1c9381fceb3525c546840bb5e63..42404f9064175d268d76441fa185ecf8d0dfad72 100644 --- a/tests/connectors/postgresql/test_validate.py +++ b/tests/connectors/postgresql/test_validate.py @@ -35,17 +35,23 @@ class TestPostgresqlValidate(TestPostgresqlDatabaseParent, CoreValidateTestMixin cls.connector.tables.drop(result) # Import here to prevent errors if database type is not installed on system. - from py_dbcn.connectors.postgresql.validate import QUOTE_IDENTIFIER_FORMAT + from py_dbcn.connectors.postgresql.validate import ( + QUOTE_COLUMN_FORMAT, + QUOTE_IDENTIFIER_FORMAT, + QUOTE_STR_LITERAL_FORMAT, + ) # Initialize variables. - cls._identifier_str = '{0}{1}{0}'.format(QUOTE_IDENTIFIER_FORMAT, '{0}') + cls._quote_columns_format = '{0}{1}{0}'.format(QUOTE_COLUMN_FORMAT, '{0}') + cls._quote_select_identifier_format = '{0}{1}{0}'.format(QUOTE_IDENTIFIER_FORMAT, '{0}') + cls._quote_str_literal_format = '{0}{1}{0}'.format(QUOTE_STR_LITERAL_FORMAT, '{0}') def test__sanitize_select_identifier_clause__success(self): """ Test sanitizing a SELECT clause, in cases when it should succeed. """ # Verify identifier str is as we expect. - self.assertText('"{0}"', self._identifier_str) + self.assertText('"{0}"', self._quote_select_identifier_format) # Call parent logic. super().test__sanitize_select_identifier_clause__success()