blob: b7612e08ab7ba853d954a880c8bfb15a64add82d [file] [log] [blame]
import re
import sys
import yaml
from collections import defaultdict
"""
@brief Wrapper function for ____run_sql_query
"""
def run_sql(sql, portid, con_args):
from madpack import ____run_sql_query
return ____run_sql_query(sql, True, portid, con_args)
"""
@brief Get the signature of a UDF/UDA for comparison
"""
def get_signature_for_compare(schema, proname, rettype, argument):
signature = '%s %s.%s(%s)' % (
rettype.strip(), schema.strip(), proname.strip(), argument.strip())
signature = re.sub('\s+', ' ', signature)
signature = re.sub('"', '', signature)
return signature.lower()
"""
@brief Base class for handling the upgrade
"""
class UpgradeBase:
def __init__(self, schema, portid, con_args):
self._schema = schema.lower()
self._portid = portid
self._con_args = con_args
self._schema_oid = None
self._get_schema_oid()
"""
@brief Wrapper function for run_sql
"""
def _run_sql(self, sql):
return run_sql(sql, self._portid, self._con_args)
"""
@brief Get the oids of some objects from the catalog in the current version
"""
def _get_schema_oid(self):
self._schema_oid = self._run_sql("""
SELECT oid FROM pg_namespace WHERE nspname = '{schema}'
""".format(schema=self._schema))[0]['oid']
"""
@brief Get the function name, return type, and arguments given an oid
@note The function can only handle the case that proallargtypes is null,
refer to pg_catalog.pg_get_function_identity_argument and
pg_catalog.pg_get_function_result in PG for a complete implementation, which are
not supported by GP
"""
def _get_function_info(self, oid):
row = self._run_sql("""
SELECT
max(proname) AS proname,
max(rettype) AS rettype,
array_to_string(
array_agg(argname || ' ' || argtype order by i), ',') AS argument
FROM
(
SELECT
proname,
textin(regtypeout(prorettype::regtype)) AS rettype,
CASE array_upper(proargtypes,1) WHEN -1 THEN ''
ELSE textin(regtypeout(unnest(proargtypes)::regtype))
END AS argtype,
CASE WHEN proargnames IS NULL THEN ''
ELSE unnest(proargnames)
END AS argname,
CASE array_upper(proargtypes,1) WHEN -1 THEN 1
ELSE generate_series(0, array_upper(proargtypes, 1))
END AS i
FROM
pg_proc AS p
WHERE
oid = {oid}
) AS f
""".format(oid=oid))
return {"proname": row[0]['proname'], 'rettype': row[0]['rettype'],
'argument': row[0]['argument']}
"""
@brief This class reads changes from the configuration file and handles
the dropping of objects
"""
class ChangeHandler(UpgradeBase):
def __init__(self, schema, portid, con_args, maddir, mad_dbrev):
UpgradeBase.__init__(self, schema, portid, con_args)
self._opr_ind_svec = None
self._get_opr_indepent_svec()
self._maddir = maddir
self._mad_dbrev = mad_dbrev
self._newmodule = None
self._udt = None
self._udf = None
self._uda = None
self._udc = None
self._load()
"""
@brief Get the UDOps which are independent of svec in the current version
"""
def _get_opr_indepent_svec(self):
rows = self._run_sql("""
SELECT
oprname,
tl.typname AS typ_left,
nspl.nspname AS nsp_left,
tr.typname AS typ_right,
nspr.nspname AS nsp_right
FROM
pg_operator AS o,
pg_type AS tl,
pg_type AS tr,
pg_namespace AS nspl,
pg_namespace AS nspr
WHERE
oprnamespace = {schema_oid} AND
(
oprleft <> '{schema_madlib}.svec'::regtype AND
oprright <> '{schema_madlib}.svec'::regtype AND
oprleft = tl.oid AND
oprright = tr.oid AND
tl.typnamespace = nspl.oid AND
tr.typnamespace = nspr.oid
)
""".format(schema_madlib=self._schema, schema_oid=self._schema_oid))
self._opr_ind_svec = {}
for row in rows:
self._opr_ind_svec[row['oprname']] = row
"""
@brief Load the configuration file
"""
def _load(self):
if float(self._mad_dbrev) < 0.6:
filename = self._maddir + '/madpack/changelist_v0.5.yaml'
else:
filename = self._maddir + '/madpack/changelist.yaml'
config = yaml.load(open(filename))
self._newmodule = config['new module']
if self._newmodule is None:
self._newmodule = {}
self._udt = config['udt']
if self._udt is None:
self._udt = {}
self._udc = config['udc']
if self._udc is None:
self._udc = {}
self._udf = defaultdict(list)
items = config['udf']
if items is not None:
for item in items:
for udf in item:
rettype = item[udf]['rettype'].lower().replace(
'schema_madlib', self._schema)
argument = ''
if item[udf]['argument'] is not None:
argument = item[udf]['argument'].lower().replace(
'schema_madlib', self._schema)
self._udf[udf].append(
{'rettype': rettype, 'argument': argument})
self._uda = defaultdict(list)
items = config['uda']
if items is not None:
for item in items:
for uda in item:
rettype = item[uda]['rettype'].lower().replace(
'schema_madlib', self._schema)
argument = ''
if item[uda]['argument'] is not None:
argument = item[uda]['argument'].lower().replace(
'schema_madlib', self._schema)
self._uda[uda].append(
{'rettype': rettype, 'argument': argument})
"""
@brief Get the list of new modules
"""
def get_newmodule(self):
return self._newmodule
"""
@brief Get the list of changed UDTs
"""
def get_udt(self):
return self._udt
"""
@brief Get the list of changed UDAs
"""
def get_uda(self):
return self._uda
"""
@brief Get the list of changed UDCs
@note This is a UDC in utilities module
"""
def get_udc(self):
return self._udc
"""
@brief Get the list of UDF signatures for comparison
"""
def get_udf_signature(self):
res = defaultdict(bool)
for udf in self._udf:
for item in self._udf[udf]:
signature = get_signature_for_compare(
self._schema, udf, item['rettype'], item['argument'])
res[signature] = True
return res
"""
@brief Get the list of UDA signatures for comparison
"""
def get_uda_signature(self):
res = defaultdict(bool)
for uda in self._uda:
for item in self._uda[uda]:
signature = get_signature_for_compare(
self._schema, uda, item['rettype'], item['argument'])
res[signature] = True
return res
"""
@brief Drop all types that were updated/removed in the new version
@note It is dangerous to drop a UDT becuase there might be many
dependencies
"""
def drop_changed_udt(self):
# Note that we use CASCADE option here. This might be dangerous because
# it may drop some undetected dependent objects (eg. UDCast, UDOp, etc)
for udt in self._udt:
# Newly added type
if self._udt[udt] == 'i':
continue
self._run_sql("""
DROP TYPE IF EXISTS {schema}.{udt} CASCADE
""".format(schema=self._schema, udt=udt))
if udt == 'svec':
# Drop operators defined in the svec module which do not
# depend on svec. We will run the whole svec.sql without
# filtering once svec changed
for opr in self._opr_ind_svec:
self._run_sql("""
DROP OPERATOR IF EXISTS {schema}.{oprname}
({nsp_left}.{typ_left}, {nsp_left}.{typ_left})
""".format(
schema=self._schema, oprname=opr,
nsp_left=self._opr_ind_svec[opr]['nsp_left'],
typ_left=self._opr_ind_svec[opr]['typ_left'],
nsp_right=self._opr_ind_svec[opr]['nsp_right'],
typ_right=self._opr_ind_svec[opr]['typ_right']
))
"""
@brief Drop all functions (UDF) that were removed in new version
"""
def drop_changed_udf(self):
for udf in self._udf:
for item in self._udf[udf]:
self._run_sql("""
DROP FUNCTION IF EXISTS {schema}.{udf}({arg})
""".format(schema=self._schema,
udf=udf,
arg=item['argument']))
"""
@brief Drop all aggregates (UDA) that were removed in new version
"""
def drop_changed_uda(self):
for uda in self._uda:
for item in self._uda[uda]:
self._run_sql("""
DROP AGGREGATE IF EXISTS {schema}.{uda}({arg})
""".format(schema=self._schema,
uda=uda,
arg=item['argument']))
"""
@brief Drop all casts (UDC) that were updated/removed in new version
@note We have special treatment for UDCs defined in the svec module
"""
def drop_changed_udc(self):
for udc in self._udc:
self._run_sql("""
DROP CAST IF EXISTS ({sourcetype} AS {targettype})
""".format(
sourcetype=self._udc[udc]['sourcetype'],
targettype=self._udc[udc]['targettype']))
"""
@brief This class detects the direct/recursive view dependencies on MADLib
UDFs/UDAs defined in the current version
"""
class ViewDependency(UpgradeBase):
def __init__(self, schema, portid, con_args):
UpgradeBase.__init__(self, schema, portid, con_args)
self._view2proc = None
self._view2view = None
self._view2def = None
self._detect_direct_view_dependency()
self._detect_recursive_view_dependency()
self._filter_recursive_view_dependency()
"""
@brief Detect direct view dependencies on MADLib UDFs/UDAs
"""
def _detect_direct_view_dependency(self):
rows = self._run_sql("""
SELECT
view, nsp.nspname AS schema, procname, procoid, proisagg
FROM
pg_namespace nsp,
(
SELECT
c.relname AS view,
c.relnamespace AS namespace,
p.proname As procname,
p.oid AS procoid,
p.proisagg AS proisagg
FROM
pg_class AS c,
pg_rewrite AS rw,
pg_depend AS d,
pg_proc AS p
WHERE
c.oid = rw.ev_class AND
rw.oid = d.objid AND
d.classid = 'pg_rewrite'::regclass AND
d.refclassid = 'pg_proc'::regclass AND
d.refobjid = p.oid AND
p.pronamespace = {schema_madlib_oid}
) t1
WHERE
t1.namespace = nsp.oid
""".format(schema_madlib_oid=self._schema_oid))
self._view2proc = defaultdict(list)
for row in rows:
key= (row['schema'], row['view'])
self._view2proc[key].append(
(row['procname'], row['procoid'],
True if row['proisagg'] == 't' else False))
"""
@brief Detect recursive view dependencies (view on view)
"""
def _detect_recursive_view_dependency(self):
rows = self._run_sql("""
SELECT
nsp1.nspname AS depender_schema,
depender,
nsp2.nspname AS dependee_schema,
dependee
FROM
pg_namespace AS nsp1,
pg_namespace AS nsp2,
(
SELECT
c.relname depender,
c.relnamespace AS depender_nsp,
c1.relname AS dependee,
c1.relnamespace AS dependee_nsp
FROM
pg_rewrite AS rw,
pg_depend AS d,
pg_class AS c,
pg_class AS c1
WHERE
rw.ev_class = c.oid AND
rw.oid = d.objid AND
d.classid = 'pg_rewrite'::regclass AND
d.refclassid = 'pg_class'::regclass AND
d.refobjid = c1.oid AND
c1.relkind = 'v' AND
c.relname <> c1.relname
GROUP BY
depender, depender_nsp, dependee, dependee_nsp
) t1
WHERE
t1.depender_nsp = nsp1.oid AND
t1.dependee_nsp = nsp2.oid
""")
self._view2view = defaultdict(list)
for row in rows:
key = (row['depender_schema'], row['depender'])
val = (row['dependee_schema'], row['dependee'])
self._view2view[key].append(val)
"""
@brief Filter out recursive view dependencies which are independent of
MADLib UDFs/UDAs
"""
def _filter_recursive_view_dependency(self):
# Get recursive dependee list
dependeelist = []
checklist = self._view2proc
while True:
dependeelist.extend(checklist.keys())
new_checklist = defaultdict(bool)
for depender in self._view2view.keys():
for dependee in self._view2view[depender]:
if dependee in checklist:
new_checklist[depender] = True
break
if len(new_checklist) == 0:
break
else:
checklist = new_checklist
# Filter recursive dependencies not related with MADLib UDF/UDAs
fil_view2view = defaultdict(list)
for depender in self._view2view:
dependee = self._view2view[depender]
dependee = [r for r in dependee if r in dependeelist]
if len(dependee) > 0:
fil_view2view[depender] = dependee
self._view2view = fil_view2view
"""
@brief Build the dependency graph (depender-to-dependee adjacency list)
"""
def _build_dependency_graph(self, hasProcDependency = False):
der2dee = self._view2view.copy()
for view in self._view2proc:
if view not in self._view2view:
der2dee[view] = []
if hasProcDependency:
der2dee[view].extend(self._view2proc[view])
graph = der2dee.copy()
for der in der2dee:
for dee in der2dee[der]:
if dee not in graph:
graph[dee] = []
return graph
"""
@brief Check dependencies
"""
def has_dependency(self):
return len(self._view2proc) > 0
"""
@brief Get the ordered views for creation
"""
def get_create_order_views(self):
graph = self._build_dependency_graph()
ordered_views = []
while True:
remove_list = []
for depender in graph:
if len(graph[depender]) == 0:
ordered_views.append(depender)
remove_list.append(depender)
for view in remove_list:
del graph[view]
for depender in graph:
graph[depender] = [r for r in graph[depender]
if r not in remove_list]
if len(remove_list) == 0:
break
return ordered_views
"""
@brief Get the ordered views for dropping
"""
def get_drop_order_views(self):
ordered_views = self.get_create_order_views()
ordered_views.reverse()
return ordered_views
"""
@brief Get the depended UDF/UDA signatures for comparison
"""
def get_depended_func_signature(self, isagg = True):
res = {}
for procs in self._view2proc.values():
for proc in procs:
if proc[2] != isagg:
continue
if (self._schema, proc) not in res:
funcinfo = self._get_function_info(proc[1])
signature = get_signature_for_compare(
self._schema, proc[0], funcinfo['rettype'], funcinfo['argument'])
res[signature] = True
return res
"""
@brief Get dependent UDAs
"""
def get_depended_uda(self):
res = []
for procs in self._view2proc.values():
for proc in procs:
if proc[2] == False:
continue
if (self._schema, proc) not in res:
res.append((self._schema, proc))
res.sort()
return res
"""
@brief Get dependent UDFs
"""
def get_depended_udf(self):
res = []
for procs in self._view2proc.values():
for proc in procs:
if proc[2] == True:
continue
if (self._schema, proc) not in res:
res.append((self._schema, proc))
res.sort()
return res
"""
@brief Save and drop the dependent views
"""
def save_and_drop(self):
self._view2def = {}
ordered_views = self.get_drop_order_views()
# Save views
for view in ordered_views:
row = self._run_sql("""
SELECT
schemaname, viewname, viewowner, definition
FROM
pg_views
WHERE
schemaname = '{schemaname}' AND
viewname = '{viewname}'
""".format(schemaname=view[0], viewname=view[1]))
self._view2def[view] = row[0]
# Drop views
for view in ordered_views:
self._run_sql("""
DROP VIEW IF EXISTS {schema}.{view}
""".format(schema=view[0], view=view[1]))
"""
@brief Restore the dependent views
"""
def restore(self):
ordered_views = self.get_create_order_views()
for view in ordered_views:
row = self._view2def[view]
schema = row['schemaname']
view = row['viewname']
owner = row['viewowner']
definition = row['definition']
self._run_sql("""
--Alter view not supported by GP, so use set/reset role as a
--workaround
--ALTER VIEW {schema}.{view} OWNER TO {owner}
SET ROLE {owner};
CREATE OR REPLACE VIEW {schema}.{view} AS {definition};
RESET ROLE
""".format(
schema=schema, view=view,
definition=definition, owner=owner))
def _node_to_str(self, node):
res = ''
if len(node) == 2:
res = '%s.%s' % (node[0], node[1])
else:
res = '%s.%s{oid=%s,isagg=%s}' % (
self._schema, node[0], node[1], node[2])
return res
def _nodes_to_str(self, nodes):
res = []
for node in nodes:
res.append(self._node_to_str(node))
return res
"""
@brief Get the dependency graph string for print
"""
def get_dependency_graph_str(self):
graph = self._build_dependency_graph(True)
nodes = graph.keys()
nodes.sort()
res = '\t\tDependency Graph (Depender-Dependee Adjacency List):\n'
for node in nodes:
res += "\t\t%s -> %s\n" % (
self._node_to_str(node), self._nodes_to_str(graph[node]))
return res[:-1]
"""
@brief This class detects the table dependencies on MADLib UDTs defined in the
current version
"""
class TableDependency(UpgradeBase):
def __init__(self, schema, portid, con_args):
UpgradeBase.__init__(self, schema, portid, con_args)
self._table2type = None
self._detect_table_dependency()
"""
@brief Detect the table dependencies on MADLib UDTs
"""
def _detect_table_dependency(self):
rows = self._run_sql("""
SELECT
nsp.nspname AS schema,
relname AS relation,
attname AS column,
typname AS type
FROM
pg_attribute a,
pg_class c,
pg_type t,
pg_namespace nsp
WHERE
t.typnamespace = {schema_madlib_oid}
AND a.atttypid = t.oid
AND c.oid = a.attrelid
AND c.relnamespace = nsp.oid
AND c.relkind = 'r'
ORDER BY
nsp.nspname, relname, attname, typname
""".format(schema_madlib_oid=self._schema_oid))
self._table2type = defaultdict(list)
for row in rows:
key= (row['schema'], row['relation'])
self._table2type[key].append(
(row['column'], row['type']))
"""
@brief Check dependencies
"""
def has_dependency(self):
return len(self._table2type) > 0
"""
@brief Get the list of depended UDTs
"""
def get_depended_udt(self):
res = defaultdict(bool)
for table in self._table2type:
for (col, typ) in self._table2type[table]:
if typ not in res:
res[typ] = True
return res
"""
@brief Get the dependencies in string for print
"""
def get_dependency_str(self):
res = '\t\tTable Dependency (schema.table.column -> type):\n'
for table in self._table2type:
for (col, udt) in self._table2type[table]:
res += "\t\t%s.%s.%s -> %s\n" % (table[0], table[1], col, udt)
return res[:-1]
"""
@brief This class removes sql statements from a sql script which should not be
executed during the upgrade
"""
class ScriptCleaner(UpgradeBase):
def __init__(self, schema, portid, con_args, change_handler):
UpgradeBase.__init__(self, schema, portid, con_args)
self._sql = None
self._existing_uda = None
self._existing_udt = None
self._get_existing_uda()
self._get_existing_udt()
self._ch = change_handler
"""
@breif Get the existing UDAs in the current version
"""
def _get_existing_uda(self):
rows = self._run_sql("""
SELECT
max(proname) AS proname,
max(rettype) AS rettype,
array_to_string(array_agg(argtype order by i), ',') AS argument
FROM
(
SELECT
p.oid AS procoid,
proname,
textin(regtypeout(prorettype::regtype)) AS rettype,
CASE array_upper(proargtypes,1) WHEN -1 THEN ''
ELSE textin(regtypeout(unnest(proargtypes)::regtype))
END AS argtype,
CASE array_upper(proargtypes,1) WHEN -1 THEN 1
ELSE generate_series(0, array_upper(proargtypes, 1))
END AS i
FROM
pg_proc AS p,
pg_namespace AS nsp
WHERE
p.pronamespace = nsp.oid AND
p.proisagg = true AND
nsp.nspname = '{schema}'
) AS f
GROUP BY
procoid
""".format(schema=self._schema))
self._existing_uda = {}
for row in rows:
# Consider about the overloaded aggregates
if row['proname'] not in self._existing_uda:
self._existing_uda[row['proname']] = []
self._existing_uda[row['proname']].append({
'rettype': ['rettype'],
'argument': row['argument']})
"""
@brief Get the existing UDTs in the current version
"""
def _get_existing_udt(self):
rows = self._run_sql("""
SELECT
typname
FROM
pg_type AS t,
pg_namespace AS nsp
WHERE
t.typnamespace = nsp.oid AND
nsp.nspname = '{schema}'
""".format(schema=self._schema))
self._existing_udt = []
for row in rows:
self._existing_udt.append(row['typname'])
"""
@note The changer_handler is needed for deciding which sql statements to
remove
"""
def get_change_handler(self):
return self._ch
"""
@brief Remove comments in the sql script
"""
def _clean_comment(self):
pattern = re.compile(r"""(/\*(.|[\r\n])*?\*/)|(--(.*|[\r\n]))""")
self._sql = re.sub(pattern, '', self._sql).strip()
"""
@breif Remove "drop/create type" statements in the sql script
"""
def _clean_type(self):
# remove 'drop type'
pattern = re.compile('DROP(\s+)TYPE(.*?);', re.DOTALL | re.IGNORECASE);
self._sql = re.sub(pattern, '', self._sql)
# remove 'create type'
udt_str = ''
for udt in self._existing_udt:
if udt in self._ch.get_udt():
continue
if udt_str == '':
udt_str += udt
else:
udt_str += '|' + udt
p_str = 'CREATE(\s+)TYPE(\s+)%s\.(%s)(.*?);' % (self._schema.upper(), udt_str)
pattern = re.compile(p_str, re.DOTALL | re.IGNORECASE);
self._sql = re.sub(pattern, '', self._sql)
"""
@brief Remove "drop/create cast" statements in the sql script
"""
def _clean_cast(self):
# remove 'drop cast'
pattern = re.compile('DROP(\s+)CAST(.*?);', re.DOTALL | re.IGNORECASE);
self._sql = re.sub(pattern, '', self._sql)
# remove 'create cast'
udc_str = ''
for udc in self._ch.get_udc():
if udc_str == '':
udc_str += '%s\s+AS\s+%s' % (
self._ch.get_udc()[udc]['sourcetype'], self._ch.get_udc()[udc]['targettype'])
else:
udc_str += '|' + '%s\s+AS\s+%s' % (
self._ch.get_udc()[udc]['sourcetype'], self._ch.get_udc()[udc]['targettype'])
pattern = re.compile('CREATE\s+CAST(.*?);', re.DOTALL | re.IGNORECASE)
if udc_str != '':
pattern = re.compile('CREATE\s+CAST\s*\(\s*(?!%s)(.*?);' % udc_str , re.DOTALL | re.IGNORECASE)
self._sql = re.sub(pattern, '', self._sql)
"""
@brief Remove "drop/create operator" statements in the sql script
"""
def _clean_operator(self):
# remove 'drop operator'
pattern = re.compile('DROP(\s+)OPERATOR(.*?);', re.DOTALL | re.IGNORECASE);
self._sql = re.sub(pattern, '', self._sql)
# remove 'create operator'
pattern = re.compile(r"""CREATE(\s+)OPERATOR(.*?);""", re.DOTALL | re.IGNORECASE);
self._sql = re.sub(pattern, '', self._sql)
"""
@brief Rewrite the type
"""
def _rewrite_type_in(self, arg):
type_mapper = {
'smallint':'(int2|smallint)',
'integer':'(int|int4|integer)',
'bigint':'(int8|bigint)',
'double precision':'(float8|double precision)',
'real':'(float4|real)',
'character varying':'(varchar|character varying)'
}
for typ in type_mapper:
arg = arg.replace(typ, type_mapper[typ])
return arg.replace('[', '\[').replace(']', '\]')
"""
@brief Remove "drop/create aggregate" statements in the sql script
"""
def _clean_aggregate(self):
# remove 'drop aggregate'
pattern = re.compile('DROP(\s+)AGGREGATE(.*?);', re.DOTALL | re.IGNORECASE);
self._sql = re.sub(pattern, '', self._sql)
# remove 'create aggregate'
uda_str = ''
for uda in self._existing_uda:
for item in self._existing_uda[uda]:
if uda in self._ch.get_uda():
items = self._ch.get_uda()[uda]
if item in items:
continue
p_arg_str = ''
argument = item['argument']
args = argument.split(',')
for arg in args:
arg = self._rewrite_type_in(arg.strip())
if p_arg_str == '':
p_arg_str += '%s\s*' % arg
else:
p_arg_str += ',\s*%s\s*' % arg
p_str = 'CREATE\s+(ORDERED\s)*\s*AGGREGATE\s+%s\.(%s)\s*\(\s*%s\)(.*?);' % (
self._schema.upper(), uda, p_arg_str)
pattern = re.compile(p_str, re.DOTALL | re.IGNORECASE);
self._sql = re.sub(pattern, '', self._sql)
"""
@brief Remove "drop function" statements and rewrite "create function"
statements in the sql script
@note We don't drop any function
"""
def _clean_function(self):
# remove 'drop function'
pattern = re.compile(r"""DROP(\s+)FUNCTION(.*?);""", re.DOTALL | re.IGNORECASE);
self._sql = re.sub(pattern, '', self._sql)
# replace 'create function' with 'create or replace function'
pattern = re.compile(r"""CREATE(\s+)FUNCTION""", re.DOTALL | re.IGNORECASE);
self._sql = re.sub(pattern, 'CREATE OR REPLACE FUNCTION', self._sql)
"""
@brief Entry function for cleaning the sql script
"""
def cleanup(self, sql):
self._sql = sql
self._clean_comment()
self._clean_type()
self._clean_cast()
self._clean_operator()
self._clean_aggregate()
self._clean_function()
return self._sql