aboutsummaryrefslogtreecommitdiff
path: root/wa/utils
diff options
context:
space:
mode:
authorWaleed El-Geresy <waleed.el-geresy@arm.com>2018-07-20 15:17:03 +0100
committerMarc Bonnici <marc.bonnici@arm.com>2018-09-12 10:13:34 +0100
commit6d654157b29fa0a5f2331483c0ba4ce879e53270 (patch)
treef23a4a0ba49cc07d0957703406460a7346ab4936 /wa/utils
parentbb255de9add4c71ca0c3ee753bfb544c99968311 (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.py219
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))