diff options
author | Waleed El-Geresy <waleed.el-geresy@arm.com> | 2018-07-20 15:17:03 +0100 |
---|---|---|
committer | Marc Bonnici <marc.bonnici@arm.com> | 2018-09-12 10:13:34 +0100 |
commit | 6d654157b29fa0a5f2331483c0ba4ce879e53270 (patch) | |
tree | f23a4a0ba49cc07d0957703406460a7346ab4936 /wa/utils | |
parent | bb255de9add4c71ca0c3ee753bfb544c99968311 (diff) |
Add Postgres Output Processor
The Output processor which is used to upload the results found in the
wa_output folder to a Postgres database, whose schema is defined by the
WA Create Database command.
Diffstat (limited to 'wa/utils')
-rw-r--r-- | wa/utils/postgres_convert.py | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/wa/utils/postgres_convert.py b/wa/utils/postgres_convert.py new file mode 100644 index 00000000..0f25b612 --- /dev/null +++ b/wa/utils/postgres_convert.py @@ -0,0 +1,219 @@ +# Copyright 2018 ARM Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +""" +This module contains additional casting and adaptation functions for several +different datatypes and metadata types for use with the psycopg2 module. The +casting functions will transform Postgresql data types into Python objects, and +the adapters the reverse. They are named this way according to the psycopg2 +conventions. + +For more information about the available adapters and casters in the standard +psycopg2 module, please see: + +http://initd.org/psycopg/docs/extensions.html#sql-adaptation-protocol-objects + +""" + +import re + +try: + from psycopg2 import InterfaceError + from psycopg2.extensions import AsIs +except ImportError as e: + InterfaceError = None + AsIs = None + +from wa.utils.types import level + + +def cast_level(value, cur): # pylint: disable=unused-argument + """Generic Level caster for psycopg2""" + if not InterfaceError: + raise ImportError('There was a problem importing psycopg2.') + if value is None: + return None + + m = re.match(r"([^\()]*)\((\d*)\)", value) + name = str(m.group(1)) + number = int(m.group(2)) + + if m: + return level(name, number) + else: + raise InterfaceError("Bad level representation: {}".format(value)) + + +def cast_vanilla(value, cur): # pylint: disable=unused-argument + """Vanilla Type caster for psycopg2 + + Simply returns the string representation. + """ + if value is None: + return None + else: + return str(value) + + +# List functions and classes for adapting + +def adapt_level(a_level): + """Generic Level Adapter for psycopg2""" + return "{}({})".format(a_level.name, a_level.value) + + +class ListOfLevel(object): + value = None + + def __init__(self, a_level): + self.value = a_level + + def return_original(self): + return self.value + + +def adapt_ListOfX(adapt_X): + """This will create a multi-column adapter for a particular type. + + Note that the type must itself need to be in array form. Therefore + this function serves to seaprate out individual lists into multiple + big lists. + E.g. if the X adapter produces array (a,b,c) + then this adapter will take an list of Xs and produce a master array: + ((a1,a2,a3),(b1,b2,b3),(c1,c2,c3)) + + Takes as its argument the adapter for the type which must produce an + SQL array string. + Note that you should NOT put the AsIs in the adapt_X function. + + The need for this function arises from the fact that we may want to + actually handle list-creating types differently if they themselves + are in a list, as in the example above, we cannot simply adopt a + recursive strategy. + + Note that master_list is the list representing the array. Each element + in the list will represent a subarray (column). If there is only one + subarray following processing then the outer {} are stripped to give a + 1 dimensional array. + """ + def adapter_function(param): + if not AsIs: + raise ImportError('There was a problem importing psycopg2.') + param = param.value + result_list = [] + for element in param: # Where param will be a list of X's + result_list.append(adapt_X(element)) + test_element = result_list[0] + num_items = len(test_element.split(",")) + master_list = [] + for x in range(num_items): + master_list.append("") + for element in result_list: + element = element.strip("{").strip("}") + element = element.split(",") + for x in range(num_items): + master_list[x] = master_list[x] + element[x] + "," + if num_items > 1: + master_sql_string = "{" + else: + master_sql_string = "" + for x in range(num_items): + # Remove trailing comma + master_list[x] = master_list[x].strip(",") + master_list[x] = "{" + master_list[x] + "}" + master_sql_string = master_sql_string + master_list[x] + "," + master_sql_string = master_sql_string.strip(",") + if num_items > 1: + master_sql_string = master_sql_string + "}" + return AsIs("'{}'".format(master_sql_string)) + return adapter_function + + +def return_as_is(adapt_X): + """Returns the AsIs appended function of the function passed + + This is useful for adapter functions intended to be used with the + adapt_ListOfX function, which must return strings, as it allows them + to be standalone adapters. + """ + if not AsIs: + raise ImportError('There was a problem importing psycopg2.') + + def adapter_function(param): + return AsIs("'{}'".format(adapt_X(param))) + return adapter_function + + +def adapt_vanilla(param): + """Vanilla adapter: simply returns the string representation""" + if not AsIs: + raise ImportError('There was a problem importing psycopg2.') + return AsIs("'{}'".format(param)) + + +def create_iterable_adapter(array_columns, explicit_iterate=False): + """Create an iterable adapter of a specified dimension + + If explicit_iterate is True, then it will be assumed that the param needs + to be iterated upon via param.iteritems(). Otherwise it will simply be + iterated vanilla. + The value of array_columns will be equal to the number of indexed elements + per item in the param iterable. E.g. a list of 3-element-long lists has + 3 elements per item in the iterable (the master list) and therefore + array_columns should be equal to 3. + If array_columns is 0, then this indicates that the iterable contains + single items. + """ + if not AsIs: + raise ImportError('There was a problem importing psycopg2.') + + def adapt_iterable(param): + """Adapts an iterable object into an SQL array""" + final_string = "" # String stores a string representation of the array + if param: + if array_columns > 1: + for index in range(array_columns): + array_string = "" + for item in param.iteritems(): + array_string = array_string + str(item[index]) + "," + array_string = array_string.strip(",") + array_string = "{" + array_string + "}" + final_string = final_string + array_string + "," + final_string = final_string.strip(",") + final_string = "{" + final_string + "}" + else: + # Simply return each item in the array + if explicit_iterate: + for item in param.iteritems(): + final_string = final_string + str(item) + "," + else: + for item in param: + final_string = final_string + str(item) + "," + final_string = "{" + final_string + "}" + return AsIs("'{}'".format(final_string)) + return adapt_iterable + + +# For reference only and future use +def adapt_list(param): + """Adapts a list into an array""" + if not AsIs: + raise ImportError('There was a problem importing psycopg2.') + final_string = "" + if param: + for item in param: + final_string = final_string + str(item) + "," + final_string = "{" + final_string + "}" + return AsIs("'{}'".format(final_string)) |