apb_extra_utils.ddl_to_scd.ddl_to_scd

  1#  coding=utf-8
  2#
  3#  Author: Ernesto Arredondo Martinez (ernestone@gmail.com)
  4#  Created: 7/6/19 18:23
  5#  Last modified: 7/6/19 18:21
  6#  Copyright (c) 2019
  7
  8import os
  9import re
 10import sys
 11
 12from ..sql_parser import x_sql_parser as SqlParser
 13
 14rex_ini_pal = r"(^|,|\(|\s|\"|')"
 15rex_fin_pal = r"($|,|\)|\s|\"|')"
 16
 17
 18def val_in_txt(search_val, a_txt):
 19    """
 20    Busca valor (search_val) en texto (a_txt) ignorando mayusculas
 21    Args:
 22        search_val:
 23        a_txt:
 24
 25    Returns:
 26        re.search
 27    """
 28    return re.search(search_val, a_txt, re.IGNORECASE)
 29
 30
 31def palabra_in_txt(palabra, a_txt):
 32    """
 33    Busca palabra (search_val) en texto (a_txt) ignorando mayusculas
 34
 35    Args:
 36        palabra:
 37        a_txt:
 38
 39    Returns:
 40        re.search
 41    """
 42    return re.search(rex_ini_pal + palabra + rex_fin_pal,
 43                     a_txt, re.IGNORECASE)
 44
 45
 46class xDdlParser(SqlParser.xSqlParser):
 47    """
 48    Clase que parsea elementos de DDL para definición de tablas
 49    """
 50    __slots__ = ('a_sql_parser', 'stmnt_create_tab', 'nom_tab', 'stmnt_grup_def_camps')
 51
 52    sql_pk = "PRIMARY KEY"
 53    SEP = SqlParser.SEP
 54    tokPunctuation = SqlParser.toks.Punctuation
 55    ApbElemStmnt = SqlParser.xElemStmntSql
 56    SqlParser = SqlParser.xSqlParser
 57
 58    def __init__(self, a_sql_text):
 59        """
 60        Inicializa el parseador a partir texto sql de una DDL de tabla
 61        Args:
 62            a_sql_text:
 63        """
 64        super(xDdlParser, self).__init__(a_sql_text)
 65
 66        self.stmnt_create_tab = self.get_stmnt("CREATE TABLE")
 67        if self.stmnt_create_tab is None:
 68            raise NameError("ERROR: El texto sql no tiene sentencia 'CREATE TABLE'")
 69
 70        self.nom_tab = self.stmnt_create_tab.id_principal
 71
 72        idx_id_nom_tab = self.stmnt_create_tab.get_elem_match_val(self.nom_tab).index
 73        self.stmnt_grup_def_camps = self.stmnt_create_tab.get_elem_post(idx_id_nom_tab).elem
 74        # Se quitan los posibles campos con '?"
 75        self.stmnt_grup_def_camps.substitute_val(r"\?", "")
 76
 77        # Se añaden campos definidos en sentencias 'ALTER TABLE nom_tab ADD (def_camp)'
 78        rex_add_col = re.compile(r"ALTER TABLE [']?" + self.nom_tab + "[']? ADD [(]", re.IGNORECASE)
 79        for a_stmnt in self.others_apb_statements():
 80            if rex_add_col.match(a_stmnt.format_val):
 81                for elem_def_camp in filter(lambda el: el.tipo != self.SEP,
 82                                            a_stmnt.get_next_elem_for_key("ADD").elem.elems_stmnt_sin_esp(1, -1)):
 83                    self.add_def_campo(elem_def_camp.format_val)
 84
 85    @property
 86    def iter_elems_key_camps(self):
 87        for a_stmnt in self.x_statements:
 88            elem_pos_pk = a_stmnt.get_elem_match_val(self.sql_pk)
 89            if elem_pos_pk is not None:
 90                stmnt_grup_camps_key = elem_pos_pk.elem.parent_stmnt.get_elem_post(elem_pos_pk.index).elem
 91
 92                if stmnt_grup_camps_key.is_group:
 93                    # Sin los parentesis y sin las comas
 94                    for el in filter(lambda el: el.tipo != self.SEP,
 95                                     stmnt_grup_camps_key.elems_stmnt_sin_esp(1, -1)):
 96                        yield el
 97
 98    @property
 99    def list_key_camps(self):
100        return [el.format_val.replace("'", "").replace('"', "") for el in self.iter_elems_key_camps]
101
102    @property
103    def iter_elems_def_camp(self):
104        for el in self.stmnt_grup_def_camps.elems_stmnt[1:-1]:
105            if el.tipo == self.SEP or palabra_in_txt(self.sql_pk, el.format_val):
106                continue
107
108            nom_camp = el.format_val.split(" ")[0].replace("'", "").replace('"', "")
109            yield nom_camp, el
110
111    @property
112    def list_def_camps(self):
113        return [el.format_val for nom_camp, el in self.iter_elems_def_camp]
114
115    def add_def_campo(self, str_def_camp):
116        """
117        Añade definición de campo
118
119        Args:
120            str_def_camp:
121        """
122        # Si se está añadiendo una Constraint entonces NO se trata como definicion de campo
123        if re.search(r"CONSTRAINT", str_def_camp, re.IGNORECASE):
124            return
125
126        elem_sep = self.ApbElemStmnt(None, self.tokPunctuation, ",")
127        self.stmnt_grup_def_camps.add_token_as_x_elem(elem_sep, -1)
128
129        a_parser_camp = self.SqlParser("(" + str_def_camp + ")")
130        a_def_camp_stmnt = a_parser_camp.x_statements[0].elems_stmnt[0].elems_stmnt[1]
131
132        self.stmnt_grup_def_camps.add_token_as_x_elem(a_def_camp_stmnt, -1)
133
134    def others_apb_statements(self):
135        for stmnt in self.x_statements:
136            if stmnt.format_val != "" and stmnt != self.stmnt_create_tab:
137                yield stmnt
138
139
140class ApbDdlToScd(object):
141    SCD4 = "SCD4"
142    SCD4c = "SCD4C"
143    SCD2 = "SCD2"
144    prefix_val_param = "A_"
145    sufix_taula_vers = "_VE"
146    sufix_seq = "_SEQ"
147    limit_noms_sql = 30
148    nom_prev_reg_vers = "PREV_REG_VERS"
149    nom_new_reg_vers = "NEW_REG_VERS"
150    nom_regaux_vers = "REGAUX"
151    nom_old_reg_vers = "OLD_REG"
152    prefix_delete_trigger = "DEL_"
153    prefix_insert_trigger = "INS_"
154    prefix_update_trigger = "UPD_"
155    prefix_primary_key = "PK_"
156    prefix_ins_upd_trigger = prefix_insert_trigger + prefix_update_trigger
157    prefix_index = "IDX_"
158    sufix_unic = "_UN"
159    sufix_trigger = "_TRG"
160    sufix_fk = "_FK"
161    sufix_check_constraint = "_CHK"
162    prefix_check_constraint = "CHK_"
163    default_date_version = "CURRENT_DATE"
164    templates_tipo = {SCD4: "template_base_SCD4.sql",
165                      SCD4c: "template_base_SCD4c.sql",
166                      SCD2: "template_base_SCD2.sql"}
167
168    __slots__ = ('ddl_parser',
169                 'nom_taula',
170                 'def_camps_taula',
171                 'nom_camps_clau',
172                 'sql_date_version')
173
174    def convert_ddl_file_to_scd(self, ddl_file,
175                                list_extra_camps=None,
176                                a_sql_date_version=None,
177                                tipo_scd='SCD4'):
178        """
179        Genera a partir de DDL de tabla de Oracle (ddl_file) un nuevo script sobre el subdirectorio 'ddl_[tipo_scd]' con el mismo nombre pero el sufijo '_VE', con los objectos SQL (create, triggers, index, ...) que permitirán la gestión de versiones de cada cambio sobre dicha tabla.
180
181        Args:
182            ddl_file: path del fichero DDL de la tabla Oracle que se quiere versionar
183            list_extra_camps: (OPC) lista con definicion de campos sql
184            a_sql_date_version: (OPC) senetencia sql que devolverá la fecha de versión. Por defecto 'CURRENT_DATE'
185            tipo_scd: (OPC) el tipo de SCD que se quiere crear. Opciones disponibles: SCD4 y SCD4C. Por defecto 'SCD4' (Slowly changing dimension 4). El tipo SCD4C añade a la tabla de versiones compound triggers de Oracle para mantener integridad fechas sin incurrir en error 'mutating tables'
186
187        Returns:
188            path_file_scd (string)
189        """
190
191        ok = self._set_parser_ddl_file(ddl_file, list_extra_camps)
192        if ok:
193            self.sql_date_version = self.default_date_version
194            if a_sql_date_version:
195                self.sql_date_version = a_sql_date_version
196
197            return self.create_ddl_scd_file(ddl_file, tipo_scd)
198
199    def _set_parser_ddl_file(self, ddl_file, list_extra_camps=None):
200        """
201
202        Args:
203            ddl_file:
204            list_extra_camps:
205
206        Returns:
207            ok (boolean)
208        """
209        self.ddl_parser = None
210        ok = False
211
212        try:
213            with open(ddl_file, encoding='utf8') as a_file:
214                a_sql_text = a_file.read()
215
216            self.ddl_parser = xDdlParser(a_sql_text)
217
218            if list_extra_camps is not None:
219                for extra_def_camp in list_extra_camps:
220                    self.ddl_parser.add_def_campo(extra_def_camp)
221
222            self._set_dades_taula()
223
224            ok = True
225        except NameError:
226            print("El fichero '" + ddl_file + "' no tiene sentencia 'CREATE TABLE'")
227
228        return ok
229
230    def _set_dades_taula(self):
231        """
232        Asigna datos de la tabla
233        """
234        self.nom_taula = self.format_nom_ddl(self.ddl_parser.nom_tab)
235
236        # Per si hi ha algun camp que és diu igual que els camps de DATA_INI i FI
237        self.ddl_parser.stmnt_grup_def_camps.substitute_val(self.nom_datini, self.nom_datini + "_BASE")
238        self.ddl_parser.stmnt_grup_def_camps.substitute_val(self.nom_datfi, self.nom_datfi + "_BASE")
239
240        self.def_camps_taula = {}
241        for nom_camp, elem_camp in self.ddl_parser.iter_elems_def_camp:
242            def_camp = elem_camp.format_val
243            vals_def_camp = def_camp.split(' ')
244            self.def_camps_taula[self.format_nom_ddl(nom_camp)] = " ".join(vals_def_camp[1:])
245
246        self.nom_camps_clau = self.ddl_parser.list_key_camps
247
248    def create_ddl_scd_file(self, ddl_file, tipo_scd):
249        """
250        Crear fichero DDL SCD según tipo
251
252        Args:
253            ddl_file:
254            tipo_scd:
255
256        Returns:
257            ddl_file_scd (string): path file SCD
258        """
259        parts_file = os.path.splitext(os.path.basename(ddl_file))
260        dir_ver = os.path.join(os.path.dirname(ddl_file), "ddls_" + tipo_scd)
261        ddl_file_ver = os.path.join(dir_ver, parts_file[0] + "_VE" + parts_file[1])
262
263        if not os.path.exists(dir_ver):
264            os.makedirs(dir_ver)
265
266        mode_io = "w"
267        if not os.path.exists(ddl_file_ver):
268            mode_io = "x"
269
270        with open(ddl_file_ver, mode_io, encoding='utf-8') as a_file:
271            a_file.write(self.ddl_parser.as_script_sql)
272            a_file.write("\n")
273            a_file.write(self.get_ddl_scd(tipo_scd=tipo_scd))
274
275            # Se añade los sql_statements al DDL de la tabla versionada que no hagan referencia a 'ALTER TABLE tab ADD'
276            rex_alter_add = re.compile(r"ALTER TABLE [']?" + self.nom_taula + "[']? ADD", re.IGNORECASE)
277            rex_nom_tab_ve = re.compile(r"[\w'\"]*" + self.nom_taula_vers + r"[\w'\"]*", re.IGNORECASE)
278            for a_stmnt in self.ddl_parser.others_apb_statements():
279                if rex_alter_add.match(a_stmnt.format_val):
280                    continue
281
282                a_elem_pos_stmnt = a_stmnt.get_elem_match_val(rex_ini_pal + self.nom_taula + rex_fin_pal)
283                if a_elem_pos_stmnt:
284                    a_elem_pos_stmnt.elem.substitute_val(self.nom_taula, self.nom_taula_vers)
285
286                id_stmnt = a_stmnt.id_principal
287                if id_stmnt is not None and not rex_nom_tab_ve.match(id_stmnt):
288                    a_stmnt.substitute_val(id_stmnt,
289                                           self.get_nom_obj_sql(id_stmnt, sufix=self.sufix_taula_vers))
290
291                a_file.write("\n\n")
292                a_file.write(a_stmnt.format_val)
293                a_file.write("\n")
294                a_file.write("/")
295                a_file.write("\n")
296
297        return ddl_file_ver
298
299    def get_ddl_tmpl(self, tipo_scd=SCD4):
300        """
301        Retorna template a usar según tipo_scd
302
303        Args:
304            tipo_scd:
305
306        Returns:
307            template_scd (string): path del template a utilizar
308        """
309        a_template_scd = ""
310        tipo_scd = tipo_scd.upper()
311
312        if tipo_scd == self.SCD4c:
313            with open(os.path.join(os.path.dirname(__file__), self.templates_tipo[self.SCD4]),
314                      encoding='utf8') as a_file:
315                a_template_scd += a_file.read()
316                a_template_scd += "\n"
317
318        with open(os.path.join(os.path.dirname(__file__), self.templates_tipo[tipo_scd]),
319                  encoding='utf8') as a_file:
320            a_template_scd += a_file.read()
321
322        return a_template_scd
323
324    def get_ddl_scd(self, tipo_scd=SCD4):
325        """
326        Retorna ddl SCD base
327        Args:
328            tipo_scd:
329
330        Returns:
331            a_ddl_scd (string): path ddl scd base
332        """
333        a_ddl_scd = self.get_ddl_tmpl(tipo_scd).format(self=self)
334
335        return a_ddl_scd
336
337    def add_property_func(self, nom_prop, a_func):
338        """
339        Añade funcion a self.__class__ para poder usar templates personalizados
340
341        Args:
342            nom_prop:
343            a_func:
344        """
345        setattr(self.__class__, nom_prop, property(a_func))
346
347    @staticmethod
348    def format_nom_ddl(nom_ddl):
349        """
350        Para cualquier string que reciba como nombre de elemento en DDL lo formatea a mayúsculas y sin "
351        Args:
352            nom_ddl:
353
354        Returns:
355            string
356        """
357        #
358        return nom_ddl.strip().replace('"', '').upper()
359
360    def clau_as_def_params_func(self, prefix_param_templ=prefix_val_param, def_param=False):
361        """
362        Retorna clave tabla como parametros de funcion de PL/SQL
363        Args:
364            prefix_param_templ:
365            def_param:
366
367        Returns:
368            string
369        """
370        sufix_param_templ = ""
371        if def_param:
372            sufix_param_templ = " " + self.nom_taula_vers + ".{nom_camp_clau}%TYPE"
373
374        param_templ = prefix_param_templ + "{nom_camp_clau}" + sufix_param_templ
375        claus_as_params = []
376        for nom_camp in self.nom_camps_clau:
377            claus_as_params.append(param_templ.format(nom_camp_clau=nom_camp))
378
379        return ", ".join(claus_as_params)
380
381    def sql_query_eq_clau(self, prefix_param=prefix_val_param):
382        """
383        Retorna clave tabla como query SQL
384        Args:
385            prefix_param:
386
387        Returns:
388            string
389        """
390        prefix_param_templ = ""
391        if prefix_param is not None:
392            prefix_param_templ = prefix_param
393
394        query_eq_clau_templ = self.nom_taula_vers + ".{nom_clau} = " + prefix_param_templ + "{nom_clau}"
395        list_query_claus = []
396        for nom_camp in self.nom_camps_clau:
397            list_query_claus.append(query_eq_clau_templ.format(nom_clau=nom_camp))
398
399        return " AND ".join(list_query_claus)
400
401    def def_set_camps_clau(self,
402                           oper_set="=",
403                           sep_set_camp=", ",
404                           prefix_camp="",
405                           prefix_val=""):
406        """
407        Retorna SQL para hacer SET de valores de los campos clave
408        Args:
409            oper_set:
410            sep_set_camp:
411            prefix_camp:
412            prefix_val:
413
414        Returns:
415            string
416        """
417        if len(prefix_camp) != 0:
418            prefix_camp += "."
419        if len(prefix_val) != 0:
420            prefix_val += "."
421
422        set_camp_templ = prefix_camp + "{nom_camp} " + oper_set + " " + prefix_val + "{nom_camp}"
423
424        list_set_camps = []
425        for a_nom_camp in self.nom_camps_clau:
426            list_set_camps.append(set_camp_templ.format(nom_camp=a_nom_camp))
427
428        return sep_set_camp.join(list_set_camps)
429
430    def def_set_camps_taula(self,
431                            oper_set="=",
432                            sep_set_camp=", ",
433                            prefix_camp="",
434                            prefix_val="",
435                            set_camps_clau=False):
436        """
437        Retorna SQL para hacer SET de los valores de los campos
438        Args:
439            oper_set:
440            sep_set_camp:
441            prefix_camp:
442            prefix_val:
443            set_camps_clau:
444
445        Returns:
446            string
447        """
448        if len(prefix_camp) != 0:
449            prefix_camp += "."
450        if len(prefix_val) != 0:
451            prefix_val += "."
452
453        set_camp_templ = prefix_camp + "{nom_camp} " + oper_set + " " + prefix_val + "{nom_camp}"
454
455        list_set_camps = []
456        for a_nom_camp in self.def_camps_taula.keys():
457            if not set_camps_clau and a_nom_camp in self.nom_camps_clau:
458                continue
459
460            list_set_camps.append(set_camp_templ.format(nom_camp=a_nom_camp))
461
462        return sep_set_camp.join(list_set_camps)
463
464    def get_nom_obj_sql(self, nom_base, prefix="", sufix=""):
465        """
466        Devuelve nombre objecto SQL con prefijo y sufijo ajustado a la longitud máxima de nombres en PL/SQL
467        Args:
468            nom_base:
469            prefix:
470            sufix:
471
472        Returns:
473            string
474        """
475        return SqlParser.get_nom_obj_sql(nom_base, prefix, sufix)
476
477    @property
478    def date_version(self):
479        return self.sql_date_version
480
481    def camps_clau(self):
482        for nom_camp in self.nom_camps_clau:
483            yield "{} {}".format(nom_camp, self.def_camps_taula.get(nom_camp))
484
485    @property
486    def def_camps_clau(self):
487        return ", ".join(self.camps_clau())
488
489    def camps_dades(self):
490        for nom_camp in sorted(self.def_camps_taula):
491            if nom_camp not in self.nom_camps_clau:
492                yield "{} {}".format(nom_camp, self.def_camps_taula.get(nom_camp))
493
494    @property
495    def def_camps_dades(self):
496        return ", ".join(self.camps_dades())
497
498    @property
499    def alter_camps_dades_taula_orig(self):
500        return "\n".join(["ALTER TABLE {nom_taula} ADD ({def_col});".format(nom_taula=self.nom_taula,
501                                                                            def_col=def_col)
502                          for def_col in self.camps_dades()])
503
504    @property
505    def alter_camps_dades_taula_vers(self):
506        return "\n".join(["ALTER TABLE {nom_taula} ADD ({def_col});".format(nom_taula=self.nom_taula_vers,
507                                                                            def_col=def_col)
508                          for def_col in self.camps_dades()])
509
510    @property
511    def str_camps_clau(self):
512        return ", ".join(self.nom_camps_clau)
513
514    @property
515    def primer_camp_clau(self):
516        return self.nom_camps_clau[0]
517
518    @property
519    def nom_taula_vers(self):
520        return self.get_nom_obj_sql(self.nom_taula, sufix=self.sufix_taula_vers)
521
522    @property
523    def nom_seq_vers(self):
524        return self.get_nom_obj_sql(self.nom_taula, sufix=self.sufix_taula_vers + self.sufix_seq)
525
526    @property
527    def def_cursor_prev_date_reg_vers(self):
528        def_curs_templ = "({params_clau}, A_DAT_VER DATE) IS " \
529                         "SELECT * FROM " + self.nom_taula_vers + \
530                         " WHERE {query_clau} AND " + \
531                         self.nom_taula_vers + "." + self.nom_datini + \
532                         " <= A_DAT_VER ORDER BY " + self.nom_datini + " DESC"
533
534        return def_curs_templ.format(params_clau=self.clau_as_def_params_func(def_param=True),
535                                     query_clau=self.sql_query_eq_clau())
536
537    @property
538    def def_cursor_actual_reg_vers(self):
539        def_curs_templ = "({params_clau}) IS " \
540                         "SELECT * FROM " + self.nom_taula_vers + \
541                         " WHERE {query_clau} AND " + \
542                         self.nom_taula_vers + "." + self.nom_datfi + \
543                         " IS NULL ORDER BY " + self.nom_datini + " DESC"
544
545        return def_curs_templ.format(params_clau=self.clau_as_def_params_func(def_param=True),
546                                     query_clau=self.sql_query_eq_clau())
547
548    @property
549    def params_cursor_clau_reg_new(self):
550        return self.clau_as_def_params_func(prefix_param_templ=":NEW.")
551
552    @property
553    def params_cursor_clau_reg_old(self):
554        return self.clau_as_def_params_func(prefix_param_templ=":OLD.")
555
556    @property
557    def sql_query_eq_clau_for_regaux(self):
558        return self.sql_query_eq_clau(prefix_param=(self.nom_regaux_vers + "."))
559
560    @property
561    def sql_query_eq_clau_for_new_reg(self):
562        return self.sql_query_eq_clau(prefix_param=":NEW.")
563
564    @property
565    def sql_query_eq_clau_for_prev_reg(self):
566        return self.sql_query_eq_clau(prefix_param=(self.nom_prev_reg_vers + "."))
567
568    @property
569    def sql_query_eq_clau_for_old_reg(self):
570        return self.sql_query_eq_clau(prefix_param=(self.nom_old_reg_vers + "."))
571
572    @property
573    def set_camps_new_reg_ver(self):
574        return self.def_set_camps_taula(oper_set=":=",
575                                        sep_set_camp=";\n",
576                                        prefix_camp=self.nom_new_reg_vers,
577                                        prefix_val=":NEW",
578                                        set_camps_clau=True)
579
580    @property
581    def set_camps_update_reg_ver(self):
582        return self.def_set_camps_taula(prefix_val=":NEW")
583
584    @property
585    def nom_trigger_del_tab_base(self):
586        return self.get_nom_obj_sql(self.nom_taula,
587                                    self.prefix_delete_trigger,
588                                    self.sufix_trigger)
589
590    @property
591    def nom_trigger_ins_upd_tab_base(self):
592        return self.get_nom_obj_sql(self.nom_taula,
593                                    self.prefix_ins_upd_trigger,
594                                    self.sufix_trigger)
595
596    @property
597    def nom_trigger_del_tab_vers(self):
598        return self.get_nom_obj_sql(self.nom_taula,
599                                    self.prefix_delete_trigger,
600                                    self.sufix_taula_vers + self.sufix_trigger)
601
602    @property
603    def nom_trigger_ins_tab_vers(self):
604        return self.get_nom_obj_sql(self.nom_taula,
605                                    self.prefix_insert_trigger,
606                                    self.sufix_taula_vers + self.sufix_trigger)
607
608    @property
609    def nom_trigger_upd_tab_vers(self):
610        return self.get_nom_obj_sql(self.nom_taula,
611                                    self.prefix_update_trigger,
612                                    self.sufix_taula_vers + self.sufix_trigger)
613
614    @property
615    def nom_constraint_chk_vers(self):
616        return self.get_nom_obj_sql(self.nom_taula,
617                                    sufix=self.sufix_check_constraint)
618
619    @property
620    def nom_var_prev_reg_vers(self):
621        return self.nom_prev_reg_vers
622
623    @property
624    def nom_var_new_reg_vers(self):
625        return self.nom_new_reg_vers
626
627    @property
628    def nom_var_regaux_vers(self):
629        return self.nom_regaux_vers
630
631    @property
632    def set_camps_clau_regaux_from_new(self):
633        return self.def_set_camps_clau(oper_set=":=",
634                                       sep_set_camp=";",
635                                       prefix_camp=self.nom_regaux_vers,
636                                       prefix_val=":NEW")
637
638    @property
639    def set_camps_clau_regaux_from_old(self):
640        return self.def_set_camps_clau(oper_set=":=",
641                                       sep_set_camp=";",
642                                       prefix_camp=self.nom_regaux_vers,
643                                       prefix_val=":OLD")
644
645    @property
646    def nom_var_old_reg_vers(self):
647        return self.nom_old_reg_vers
648
649    @property
650    def set_camps_oldreg_from_old(self):
651        return self.def_set_camps_taula(oper_set=":=",
652                                        sep_set_camp=";\n",
653                                        prefix_camp=self.nom_old_reg_vers,
654                                        prefix_val=":OLD",
655                                        set_camps_clau=True)
656
657    @property
658    def nom_datini(self):
659        return "DAT_INI_VER"
660
661    @property
662    def nom_datfi(self):
663        return "DAT_FI_VER"
664
665    @property
666    def nom_idx_datini(self):
667        return self.get_nom_obj_sql(self.nom_taula, self.prefix_index, self.sufix_taula_vers + "_" + self.nom_datini)
668
669    @property
670    def nom_idx_datfi(self):
671        return self.get_nom_obj_sql(self.nom_taula, self.prefix_index, self.sufix_taula_vers + "_" + self.nom_datfi)
672
673    @property
674    def nom_taula_vers_pk(self):
675        return self.get_nom_obj_sql(self.nom_taula, self.prefix_primary_key, self.sufix_taula_vers)
676
677    @property
678    def idx_nom_taula_vers_pk_datini(self):
679        return self.get_nom_obj_sql(self.nom_taula, "PKDAT_", self.sufix_taula_vers + self.sufix_unic)
680
681    @property
682    def nom_taula_vers_datini_chk(self):
683        return self.get_nom_obj_sql(self.nom_taula, "DAT_", self.sufix_taula_vers + self.sufix_check_constraint)
684
685    def get_triggers_extra_tab_base(self):
686        """
687        (SUBCLASEAR) Retorna lista strings con las definiciones de triggers extra a incluir
688        """
689        return []
690
691    @property
692    def triggers_extra_tab_base(self):
693        str_ret = ""
694        l_trgs = self.get_triggers_extra_tab_base()
695        if l_trgs:
696            str_ret = "{a_sql}".format(a_sql="\n".join(l_trgs))
697
698        return str_ret
699
700    def get_follows_trigger_ins_upd_tab_base(self):
701        """
702        (SUBCLASEAR) Retorna lista de strings con los nombres de triggers que precederán al trigger de la tabla
703        base que se encargará del versionado (el del nombre dado por property 'self.nom_nom_trigger_ins_upd_tab_base')
704        """
705        return []
706
707    @property
708    def follows_trigger_ins_upd_tab_base(self):
709        str_ret = ""
710        l_trgs = self.get_follows_trigger_ins_upd_tab_base()
711        if l_trgs:
712            str_ret = "\nFOLLOWS {a_sql}".format(a_sql=", ".join(l_trgs))
713
714        return str_ret
715
716
717if __name__ == '__main__':
718    a_eng = ApbDdlToScd()
719
720    import fire
721    path_scd = fire.Fire(a_eng.convert_ddl_file_to_scd)
722
723    ret = 1
724    if path_scd:
725        print("Fichero '{path_scd}' creado con éxito".format(path_scd=path_scd))
726        ret = 0
727
728    sys.exit(ret)
rex_ini_pal = '(^|,|\\(|\\s|\\"|\')'
rex_fin_pal = '($|,|\\)|\\s|\\"|\')'
def val_in_txt(search_val, a_txt):
19def val_in_txt(search_val, a_txt):
20    """
21    Busca valor (search_val) en texto (a_txt) ignorando mayusculas
22    Args:
23        search_val:
24        a_txt:
25
26    Returns:
27        re.search
28    """
29    return re.search(search_val, a_txt, re.IGNORECASE)

Busca valor (search_val) en texto (a_txt) ignorando mayusculas

Arguments:
  • search_val:
  • a_txt:
Returns:

re.search

def palabra_in_txt(palabra, a_txt):
32def palabra_in_txt(palabra, a_txt):
33    """
34    Busca palabra (search_val) en texto (a_txt) ignorando mayusculas
35
36    Args:
37        palabra:
38        a_txt:
39
40    Returns:
41        re.search
42    """
43    return re.search(rex_ini_pal + palabra + rex_fin_pal,
44                     a_txt, re.IGNORECASE)

Busca palabra (search_val) en texto (a_txt) ignorando mayusculas

Arguments:
  • palabra:
  • a_txt:
Returns:

re.search

 47class xDdlParser(SqlParser.xSqlParser):
 48    """
 49    Clase que parsea elementos de DDL para definición de tablas
 50    """
 51    __slots__ = ('a_sql_parser', 'stmnt_create_tab', 'nom_tab', 'stmnt_grup_def_camps')
 52
 53    sql_pk = "PRIMARY KEY"
 54    SEP = SqlParser.SEP
 55    tokPunctuation = SqlParser.toks.Punctuation
 56    ApbElemStmnt = SqlParser.xElemStmntSql
 57    SqlParser = SqlParser.xSqlParser
 58
 59    def __init__(self, a_sql_text):
 60        """
 61        Inicializa el parseador a partir texto sql de una DDL de tabla
 62        Args:
 63            a_sql_text:
 64        """
 65        super(xDdlParser, self).__init__(a_sql_text)
 66
 67        self.stmnt_create_tab = self.get_stmnt("CREATE TABLE")
 68        if self.stmnt_create_tab is None:
 69            raise NameError("ERROR: El texto sql no tiene sentencia 'CREATE TABLE'")
 70
 71        self.nom_tab = self.stmnt_create_tab.id_principal
 72
 73        idx_id_nom_tab = self.stmnt_create_tab.get_elem_match_val(self.nom_tab).index
 74        self.stmnt_grup_def_camps = self.stmnt_create_tab.get_elem_post(idx_id_nom_tab).elem
 75        # Se quitan los posibles campos con '?"
 76        self.stmnt_grup_def_camps.substitute_val(r"\?", "")
 77
 78        # Se añaden campos definidos en sentencias 'ALTER TABLE nom_tab ADD (def_camp)'
 79        rex_add_col = re.compile(r"ALTER TABLE [']?" + self.nom_tab + "[']? ADD [(]", re.IGNORECASE)
 80        for a_stmnt in self.others_apb_statements():
 81            if rex_add_col.match(a_stmnt.format_val):
 82                for elem_def_camp in filter(lambda el: el.tipo != self.SEP,
 83                                            a_stmnt.get_next_elem_for_key("ADD").elem.elems_stmnt_sin_esp(1, -1)):
 84                    self.add_def_campo(elem_def_camp.format_val)
 85
 86    @property
 87    def iter_elems_key_camps(self):
 88        for a_stmnt in self.x_statements:
 89            elem_pos_pk = a_stmnt.get_elem_match_val(self.sql_pk)
 90            if elem_pos_pk is not None:
 91                stmnt_grup_camps_key = elem_pos_pk.elem.parent_stmnt.get_elem_post(elem_pos_pk.index).elem
 92
 93                if stmnt_grup_camps_key.is_group:
 94                    # Sin los parentesis y sin las comas
 95                    for el in filter(lambda el: el.tipo != self.SEP,
 96                                     stmnt_grup_camps_key.elems_stmnt_sin_esp(1, -1)):
 97                        yield el
 98
 99    @property
100    def list_key_camps(self):
101        return [el.format_val.replace("'", "").replace('"', "") for el in self.iter_elems_key_camps]
102
103    @property
104    def iter_elems_def_camp(self):
105        for el in self.stmnt_grup_def_camps.elems_stmnt[1:-1]:
106            if el.tipo == self.SEP or palabra_in_txt(self.sql_pk, el.format_val):
107                continue
108
109            nom_camp = el.format_val.split(" ")[0].replace("'", "").replace('"', "")
110            yield nom_camp, el
111
112    @property
113    def list_def_camps(self):
114        return [el.format_val for nom_camp, el in self.iter_elems_def_camp]
115
116    def add_def_campo(self, str_def_camp):
117        """
118        Añade definición de campo
119
120        Args:
121            str_def_camp:
122        """
123        # Si se está añadiendo una Constraint entonces NO se trata como definicion de campo
124        if re.search(r"CONSTRAINT", str_def_camp, re.IGNORECASE):
125            return
126
127        elem_sep = self.ApbElemStmnt(None, self.tokPunctuation, ",")
128        self.stmnt_grup_def_camps.add_token_as_x_elem(elem_sep, -1)
129
130        a_parser_camp = self.SqlParser("(" + str_def_camp + ")")
131        a_def_camp_stmnt = a_parser_camp.x_statements[0].elems_stmnt[0].elems_stmnt[1]
132
133        self.stmnt_grup_def_camps.add_token_as_x_elem(a_def_camp_stmnt, -1)
134
135    def others_apb_statements(self):
136        for stmnt in self.x_statements:
137            if stmnt.format_val != "" and stmnt != self.stmnt_create_tab:
138                yield stmnt

Clase que parsea elementos de DDL para definición de tablas

xDdlParser(a_sql_text)
59    def __init__(self, a_sql_text):
60        """
61        Inicializa el parseador a partir texto sql de una DDL de tabla
62        Args:
63            a_sql_text:
64        """
65        super(xDdlParser, self).__init__(a_sql_text)
66
67        self.stmnt_create_tab = self.get_stmnt("CREATE TABLE")
68        if self.stmnt_create_tab is None:
69            raise NameError("ERROR: El texto sql no tiene sentencia 'CREATE TABLE'")
70
71        self.nom_tab = self.stmnt_create_tab.id_principal
72
73        idx_id_nom_tab = self.stmnt_create_tab.get_elem_match_val(self.nom_tab).index
74        self.stmnt_grup_def_camps = self.stmnt_create_tab.get_elem_post(idx_id_nom_tab).elem
75        # Se quitan los posibles campos con '?"
76        self.stmnt_grup_def_camps.substitute_val(r"\?", "")
77
78        # Se añaden campos definidos en sentencias 'ALTER TABLE nom_tab ADD (def_camp)'
79        rex_add_col = re.compile(r"ALTER TABLE [']?" + self.nom_tab + "[']? ADD [(]", re.IGNORECASE)
80        for a_stmnt in self.others_apb_statements():
81            if rex_add_col.match(a_stmnt.format_val):
82                for elem_def_camp in filter(lambda el: el.tipo != self.SEP,
83                                            a_stmnt.get_next_elem_for_key("ADD").elem.elems_stmnt_sin_esp(1, -1)):
84                    self.add_def_campo(elem_def_camp.format_val)

Inicializa el parseador a partir texto sql de una DDL de tabla

Arguments:
  • a_sql_text:
sql_pk = 'PRIMARY KEY'
SEP = 'sep'
tokPunctuation = Token.Punctuation
stmnt_create_tab
nom_tab
stmnt_grup_def_camps
iter_elems_key_camps
86    @property
87    def iter_elems_key_camps(self):
88        for a_stmnt in self.x_statements:
89            elem_pos_pk = a_stmnt.get_elem_match_val(self.sql_pk)
90            if elem_pos_pk is not None:
91                stmnt_grup_camps_key = elem_pos_pk.elem.parent_stmnt.get_elem_post(elem_pos_pk.index).elem
92
93                if stmnt_grup_camps_key.is_group:
94                    # Sin los parentesis y sin las comas
95                    for el in filter(lambda el: el.tipo != self.SEP,
96                                     stmnt_grup_camps_key.elems_stmnt_sin_esp(1, -1)):
97                        yield el
list_key_camps
 99    @property
100    def list_key_camps(self):
101        return [el.format_val.replace("'", "").replace('"', "") for el in self.iter_elems_key_camps]
iter_elems_def_camp
103    @property
104    def iter_elems_def_camp(self):
105        for el in self.stmnt_grup_def_camps.elems_stmnt[1:-1]:
106            if el.tipo == self.SEP or palabra_in_txt(self.sql_pk, el.format_val):
107                continue
108
109            nom_camp = el.format_val.split(" ")[0].replace("'", "").replace('"', "")
110            yield nom_camp, el
list_def_camps
112    @property
113    def list_def_camps(self):
114        return [el.format_val for nom_camp, el in self.iter_elems_def_camp]
def add_def_campo(self, str_def_camp):
116    def add_def_campo(self, str_def_camp):
117        """
118        Añade definición de campo
119
120        Args:
121            str_def_camp:
122        """
123        # Si se está añadiendo una Constraint entonces NO se trata como definicion de campo
124        if re.search(r"CONSTRAINT", str_def_camp, re.IGNORECASE):
125            return
126
127        elem_sep = self.ApbElemStmnt(None, self.tokPunctuation, ",")
128        self.stmnt_grup_def_camps.add_token_as_x_elem(elem_sep, -1)
129
130        a_parser_camp = self.SqlParser("(" + str_def_camp + ")")
131        a_def_camp_stmnt = a_parser_camp.x_statements[0].elems_stmnt[0].elems_stmnt[1]
132
133        self.stmnt_grup_def_camps.add_token_as_x_elem(a_def_camp_stmnt, -1)

Añade definición de campo

Arguments:
  • str_def_camp:
def others_apb_statements(self):
135    def others_apb_statements(self):
136        for stmnt in self.x_statements:
137            if stmnt.format_val != "" and stmnt != self.stmnt_create_tab:
138                yield stmnt
a_sql_parser
class ApbDdlToScd:
141class ApbDdlToScd(object):
142    SCD4 = "SCD4"
143    SCD4c = "SCD4C"
144    SCD2 = "SCD2"
145    prefix_val_param = "A_"
146    sufix_taula_vers = "_VE"
147    sufix_seq = "_SEQ"
148    limit_noms_sql = 30
149    nom_prev_reg_vers = "PREV_REG_VERS"
150    nom_new_reg_vers = "NEW_REG_VERS"
151    nom_regaux_vers = "REGAUX"
152    nom_old_reg_vers = "OLD_REG"
153    prefix_delete_trigger = "DEL_"
154    prefix_insert_trigger = "INS_"
155    prefix_update_trigger = "UPD_"
156    prefix_primary_key = "PK_"
157    prefix_ins_upd_trigger = prefix_insert_trigger + prefix_update_trigger
158    prefix_index = "IDX_"
159    sufix_unic = "_UN"
160    sufix_trigger = "_TRG"
161    sufix_fk = "_FK"
162    sufix_check_constraint = "_CHK"
163    prefix_check_constraint = "CHK_"
164    default_date_version = "CURRENT_DATE"
165    templates_tipo = {SCD4: "template_base_SCD4.sql",
166                      SCD4c: "template_base_SCD4c.sql",
167                      SCD2: "template_base_SCD2.sql"}
168
169    __slots__ = ('ddl_parser',
170                 'nom_taula',
171                 'def_camps_taula',
172                 'nom_camps_clau',
173                 'sql_date_version')
174
175    def convert_ddl_file_to_scd(self, ddl_file,
176                                list_extra_camps=None,
177                                a_sql_date_version=None,
178                                tipo_scd='SCD4'):
179        """
180        Genera a partir de DDL de tabla de Oracle (ddl_file) un nuevo script sobre el subdirectorio 'ddl_[tipo_scd]' con el mismo nombre pero el sufijo '_VE', con los objectos SQL (create, triggers, index, ...) que permitirán la gestión de versiones de cada cambio sobre dicha tabla.
181
182        Args:
183            ddl_file: path del fichero DDL de la tabla Oracle que se quiere versionar
184            list_extra_camps: (OPC) lista con definicion de campos sql
185            a_sql_date_version: (OPC) senetencia sql que devolverá la fecha de versión. Por defecto 'CURRENT_DATE'
186            tipo_scd: (OPC) el tipo de SCD que se quiere crear. Opciones disponibles: SCD4 y SCD4C. Por defecto 'SCD4' (Slowly changing dimension 4). El tipo SCD4C añade a la tabla de versiones compound triggers de Oracle para mantener integridad fechas sin incurrir en error 'mutating tables'
187
188        Returns:
189            path_file_scd (string)
190        """
191
192        ok = self._set_parser_ddl_file(ddl_file, list_extra_camps)
193        if ok:
194            self.sql_date_version = self.default_date_version
195            if a_sql_date_version:
196                self.sql_date_version = a_sql_date_version
197
198            return self.create_ddl_scd_file(ddl_file, tipo_scd)
199
200    def _set_parser_ddl_file(self, ddl_file, list_extra_camps=None):
201        """
202
203        Args:
204            ddl_file:
205            list_extra_camps:
206
207        Returns:
208            ok (boolean)
209        """
210        self.ddl_parser = None
211        ok = False
212
213        try:
214            with open(ddl_file, encoding='utf8') as a_file:
215                a_sql_text = a_file.read()
216
217            self.ddl_parser = xDdlParser(a_sql_text)
218
219            if list_extra_camps is not None:
220                for extra_def_camp in list_extra_camps:
221                    self.ddl_parser.add_def_campo(extra_def_camp)
222
223            self._set_dades_taula()
224
225            ok = True
226        except NameError:
227            print("El fichero '" + ddl_file + "' no tiene sentencia 'CREATE TABLE'")
228
229        return ok
230
231    def _set_dades_taula(self):
232        """
233        Asigna datos de la tabla
234        """
235        self.nom_taula = self.format_nom_ddl(self.ddl_parser.nom_tab)
236
237        # Per si hi ha algun camp que és diu igual que els camps de DATA_INI i FI
238        self.ddl_parser.stmnt_grup_def_camps.substitute_val(self.nom_datini, self.nom_datini + "_BASE")
239        self.ddl_parser.stmnt_grup_def_camps.substitute_val(self.nom_datfi, self.nom_datfi + "_BASE")
240
241        self.def_camps_taula = {}
242        for nom_camp, elem_camp in self.ddl_parser.iter_elems_def_camp:
243            def_camp = elem_camp.format_val
244            vals_def_camp = def_camp.split(' ')
245            self.def_camps_taula[self.format_nom_ddl(nom_camp)] = " ".join(vals_def_camp[1:])
246
247        self.nom_camps_clau = self.ddl_parser.list_key_camps
248
249    def create_ddl_scd_file(self, ddl_file, tipo_scd):
250        """
251        Crear fichero DDL SCD según tipo
252
253        Args:
254            ddl_file:
255            tipo_scd:
256
257        Returns:
258            ddl_file_scd (string): path file SCD
259        """
260        parts_file = os.path.splitext(os.path.basename(ddl_file))
261        dir_ver = os.path.join(os.path.dirname(ddl_file), "ddls_" + tipo_scd)
262        ddl_file_ver = os.path.join(dir_ver, parts_file[0] + "_VE" + parts_file[1])
263
264        if not os.path.exists(dir_ver):
265            os.makedirs(dir_ver)
266
267        mode_io = "w"
268        if not os.path.exists(ddl_file_ver):
269            mode_io = "x"
270
271        with open(ddl_file_ver, mode_io, encoding='utf-8') as a_file:
272            a_file.write(self.ddl_parser.as_script_sql)
273            a_file.write("\n")
274            a_file.write(self.get_ddl_scd(tipo_scd=tipo_scd))
275
276            # Se añade los sql_statements al DDL de la tabla versionada que no hagan referencia a 'ALTER TABLE tab ADD'
277            rex_alter_add = re.compile(r"ALTER TABLE [']?" + self.nom_taula + "[']? ADD", re.IGNORECASE)
278            rex_nom_tab_ve = re.compile(r"[\w'\"]*" + self.nom_taula_vers + r"[\w'\"]*", re.IGNORECASE)
279            for a_stmnt in self.ddl_parser.others_apb_statements():
280                if rex_alter_add.match(a_stmnt.format_val):
281                    continue
282
283                a_elem_pos_stmnt = a_stmnt.get_elem_match_val(rex_ini_pal + self.nom_taula + rex_fin_pal)
284                if a_elem_pos_stmnt:
285                    a_elem_pos_stmnt.elem.substitute_val(self.nom_taula, self.nom_taula_vers)
286
287                id_stmnt = a_stmnt.id_principal
288                if id_stmnt is not None and not rex_nom_tab_ve.match(id_stmnt):
289                    a_stmnt.substitute_val(id_stmnt,
290                                           self.get_nom_obj_sql(id_stmnt, sufix=self.sufix_taula_vers))
291
292                a_file.write("\n\n")
293                a_file.write(a_stmnt.format_val)
294                a_file.write("\n")
295                a_file.write("/")
296                a_file.write("\n")
297
298        return ddl_file_ver
299
300    def get_ddl_tmpl(self, tipo_scd=SCD4):
301        """
302        Retorna template a usar según tipo_scd
303
304        Args:
305            tipo_scd:
306
307        Returns:
308            template_scd (string): path del template a utilizar
309        """
310        a_template_scd = ""
311        tipo_scd = tipo_scd.upper()
312
313        if tipo_scd == self.SCD4c:
314            with open(os.path.join(os.path.dirname(__file__), self.templates_tipo[self.SCD4]),
315                      encoding='utf8') as a_file:
316                a_template_scd += a_file.read()
317                a_template_scd += "\n"
318
319        with open(os.path.join(os.path.dirname(__file__), self.templates_tipo[tipo_scd]),
320                  encoding='utf8') as a_file:
321            a_template_scd += a_file.read()
322
323        return a_template_scd
324
325    def get_ddl_scd(self, tipo_scd=SCD4):
326        """
327        Retorna ddl SCD base
328        Args:
329            tipo_scd:
330
331        Returns:
332            a_ddl_scd (string): path ddl scd base
333        """
334        a_ddl_scd = self.get_ddl_tmpl(tipo_scd).format(self=self)
335
336        return a_ddl_scd
337
338    def add_property_func(self, nom_prop, a_func):
339        """
340        Añade funcion a self.__class__ para poder usar templates personalizados
341
342        Args:
343            nom_prop:
344            a_func:
345        """
346        setattr(self.__class__, nom_prop, property(a_func))
347
348    @staticmethod
349    def format_nom_ddl(nom_ddl):
350        """
351        Para cualquier string que reciba como nombre de elemento en DDL lo formatea a mayúsculas y sin "
352        Args:
353            nom_ddl:
354
355        Returns:
356            string
357        """
358        #
359        return nom_ddl.strip().replace('"', '').upper()
360
361    def clau_as_def_params_func(self, prefix_param_templ=prefix_val_param, def_param=False):
362        """
363        Retorna clave tabla como parametros de funcion de PL/SQL
364        Args:
365            prefix_param_templ:
366            def_param:
367
368        Returns:
369            string
370        """
371        sufix_param_templ = ""
372        if def_param:
373            sufix_param_templ = " " + self.nom_taula_vers + ".{nom_camp_clau}%TYPE"
374
375        param_templ = prefix_param_templ + "{nom_camp_clau}" + sufix_param_templ
376        claus_as_params = []
377        for nom_camp in self.nom_camps_clau:
378            claus_as_params.append(param_templ.format(nom_camp_clau=nom_camp))
379
380        return ", ".join(claus_as_params)
381
382    def sql_query_eq_clau(self, prefix_param=prefix_val_param):
383        """
384        Retorna clave tabla como query SQL
385        Args:
386            prefix_param:
387
388        Returns:
389            string
390        """
391        prefix_param_templ = ""
392        if prefix_param is not None:
393            prefix_param_templ = prefix_param
394
395        query_eq_clau_templ = self.nom_taula_vers + ".{nom_clau} = " + prefix_param_templ + "{nom_clau}"
396        list_query_claus = []
397        for nom_camp in self.nom_camps_clau:
398            list_query_claus.append(query_eq_clau_templ.format(nom_clau=nom_camp))
399
400        return " AND ".join(list_query_claus)
401
402    def def_set_camps_clau(self,
403                           oper_set="=",
404                           sep_set_camp=", ",
405                           prefix_camp="",
406                           prefix_val=""):
407        """
408        Retorna SQL para hacer SET de valores de los campos clave
409        Args:
410            oper_set:
411            sep_set_camp:
412            prefix_camp:
413            prefix_val:
414
415        Returns:
416            string
417        """
418        if len(prefix_camp) != 0:
419            prefix_camp += "."
420        if len(prefix_val) != 0:
421            prefix_val += "."
422
423        set_camp_templ = prefix_camp + "{nom_camp} " + oper_set + " " + prefix_val + "{nom_camp}"
424
425        list_set_camps = []
426        for a_nom_camp in self.nom_camps_clau:
427            list_set_camps.append(set_camp_templ.format(nom_camp=a_nom_camp))
428
429        return sep_set_camp.join(list_set_camps)
430
431    def def_set_camps_taula(self,
432                            oper_set="=",
433                            sep_set_camp=", ",
434                            prefix_camp="",
435                            prefix_val="",
436                            set_camps_clau=False):
437        """
438        Retorna SQL para hacer SET de los valores de los campos
439        Args:
440            oper_set:
441            sep_set_camp:
442            prefix_camp:
443            prefix_val:
444            set_camps_clau:
445
446        Returns:
447            string
448        """
449        if len(prefix_camp) != 0:
450            prefix_camp += "."
451        if len(prefix_val) != 0:
452            prefix_val += "."
453
454        set_camp_templ = prefix_camp + "{nom_camp} " + oper_set + " " + prefix_val + "{nom_camp}"
455
456        list_set_camps = []
457        for a_nom_camp in self.def_camps_taula.keys():
458            if not set_camps_clau and a_nom_camp in self.nom_camps_clau:
459                continue
460
461            list_set_camps.append(set_camp_templ.format(nom_camp=a_nom_camp))
462
463        return sep_set_camp.join(list_set_camps)
464
465    def get_nom_obj_sql(self, nom_base, prefix="", sufix=""):
466        """
467        Devuelve nombre objecto SQL con prefijo y sufijo ajustado a la longitud máxima de nombres en PL/SQL
468        Args:
469            nom_base:
470            prefix:
471            sufix:
472
473        Returns:
474            string
475        """
476        return SqlParser.get_nom_obj_sql(nom_base, prefix, sufix)
477
478    @property
479    def date_version(self):
480        return self.sql_date_version
481
482    def camps_clau(self):
483        for nom_camp in self.nom_camps_clau:
484            yield "{} {}".format(nom_camp, self.def_camps_taula.get(nom_camp))
485
486    @property
487    def def_camps_clau(self):
488        return ", ".join(self.camps_clau())
489
490    def camps_dades(self):
491        for nom_camp in sorted(self.def_camps_taula):
492            if nom_camp not in self.nom_camps_clau:
493                yield "{} {}".format(nom_camp, self.def_camps_taula.get(nom_camp))
494
495    @property
496    def def_camps_dades(self):
497        return ", ".join(self.camps_dades())
498
499    @property
500    def alter_camps_dades_taula_orig(self):
501        return "\n".join(["ALTER TABLE {nom_taula} ADD ({def_col});".format(nom_taula=self.nom_taula,
502                                                                            def_col=def_col)
503                          for def_col in self.camps_dades()])
504
505    @property
506    def alter_camps_dades_taula_vers(self):
507        return "\n".join(["ALTER TABLE {nom_taula} ADD ({def_col});".format(nom_taula=self.nom_taula_vers,
508                                                                            def_col=def_col)
509                          for def_col in self.camps_dades()])
510
511    @property
512    def str_camps_clau(self):
513        return ", ".join(self.nom_camps_clau)
514
515    @property
516    def primer_camp_clau(self):
517        return self.nom_camps_clau[0]
518
519    @property
520    def nom_taula_vers(self):
521        return self.get_nom_obj_sql(self.nom_taula, sufix=self.sufix_taula_vers)
522
523    @property
524    def nom_seq_vers(self):
525        return self.get_nom_obj_sql(self.nom_taula, sufix=self.sufix_taula_vers + self.sufix_seq)
526
527    @property
528    def def_cursor_prev_date_reg_vers(self):
529        def_curs_templ = "({params_clau}, A_DAT_VER DATE) IS " \
530                         "SELECT * FROM " + self.nom_taula_vers + \
531                         " WHERE {query_clau} AND " + \
532                         self.nom_taula_vers + "." + self.nom_datini + \
533                         " <= A_DAT_VER ORDER BY " + self.nom_datini + " DESC"
534
535        return def_curs_templ.format(params_clau=self.clau_as_def_params_func(def_param=True),
536                                     query_clau=self.sql_query_eq_clau())
537
538    @property
539    def def_cursor_actual_reg_vers(self):
540        def_curs_templ = "({params_clau}) IS " \
541                         "SELECT * FROM " + self.nom_taula_vers + \
542                         " WHERE {query_clau} AND " + \
543                         self.nom_taula_vers + "." + self.nom_datfi + \
544                         " IS NULL ORDER BY " + self.nom_datini + " DESC"
545
546        return def_curs_templ.format(params_clau=self.clau_as_def_params_func(def_param=True),
547                                     query_clau=self.sql_query_eq_clau())
548
549    @property
550    def params_cursor_clau_reg_new(self):
551        return self.clau_as_def_params_func(prefix_param_templ=":NEW.")
552
553    @property
554    def params_cursor_clau_reg_old(self):
555        return self.clau_as_def_params_func(prefix_param_templ=":OLD.")
556
557    @property
558    def sql_query_eq_clau_for_regaux(self):
559        return self.sql_query_eq_clau(prefix_param=(self.nom_regaux_vers + "."))
560
561    @property
562    def sql_query_eq_clau_for_new_reg(self):
563        return self.sql_query_eq_clau(prefix_param=":NEW.")
564
565    @property
566    def sql_query_eq_clau_for_prev_reg(self):
567        return self.sql_query_eq_clau(prefix_param=(self.nom_prev_reg_vers + "."))
568
569    @property
570    def sql_query_eq_clau_for_old_reg(self):
571        return self.sql_query_eq_clau(prefix_param=(self.nom_old_reg_vers + "."))
572
573    @property
574    def set_camps_new_reg_ver(self):
575        return self.def_set_camps_taula(oper_set=":=",
576                                        sep_set_camp=";\n",
577                                        prefix_camp=self.nom_new_reg_vers,
578                                        prefix_val=":NEW",
579                                        set_camps_clau=True)
580
581    @property
582    def set_camps_update_reg_ver(self):
583        return self.def_set_camps_taula(prefix_val=":NEW")
584
585    @property
586    def nom_trigger_del_tab_base(self):
587        return self.get_nom_obj_sql(self.nom_taula,
588                                    self.prefix_delete_trigger,
589                                    self.sufix_trigger)
590
591    @property
592    def nom_trigger_ins_upd_tab_base(self):
593        return self.get_nom_obj_sql(self.nom_taula,
594                                    self.prefix_ins_upd_trigger,
595                                    self.sufix_trigger)
596
597    @property
598    def nom_trigger_del_tab_vers(self):
599        return self.get_nom_obj_sql(self.nom_taula,
600                                    self.prefix_delete_trigger,
601                                    self.sufix_taula_vers + self.sufix_trigger)
602
603    @property
604    def nom_trigger_ins_tab_vers(self):
605        return self.get_nom_obj_sql(self.nom_taula,
606                                    self.prefix_insert_trigger,
607                                    self.sufix_taula_vers + self.sufix_trigger)
608
609    @property
610    def nom_trigger_upd_tab_vers(self):
611        return self.get_nom_obj_sql(self.nom_taula,
612                                    self.prefix_update_trigger,
613                                    self.sufix_taula_vers + self.sufix_trigger)
614
615    @property
616    def nom_constraint_chk_vers(self):
617        return self.get_nom_obj_sql(self.nom_taula,
618                                    sufix=self.sufix_check_constraint)
619
620    @property
621    def nom_var_prev_reg_vers(self):
622        return self.nom_prev_reg_vers
623
624    @property
625    def nom_var_new_reg_vers(self):
626        return self.nom_new_reg_vers
627
628    @property
629    def nom_var_regaux_vers(self):
630        return self.nom_regaux_vers
631
632    @property
633    def set_camps_clau_regaux_from_new(self):
634        return self.def_set_camps_clau(oper_set=":=",
635                                       sep_set_camp=";",
636                                       prefix_camp=self.nom_regaux_vers,
637                                       prefix_val=":NEW")
638
639    @property
640    def set_camps_clau_regaux_from_old(self):
641        return self.def_set_camps_clau(oper_set=":=",
642                                       sep_set_camp=";",
643                                       prefix_camp=self.nom_regaux_vers,
644                                       prefix_val=":OLD")
645
646    @property
647    def nom_var_old_reg_vers(self):
648        return self.nom_old_reg_vers
649
650    @property
651    def set_camps_oldreg_from_old(self):
652        return self.def_set_camps_taula(oper_set=":=",
653                                        sep_set_camp=";\n",
654                                        prefix_camp=self.nom_old_reg_vers,
655                                        prefix_val=":OLD",
656                                        set_camps_clau=True)
657
658    @property
659    def nom_datini(self):
660        return "DAT_INI_VER"
661
662    @property
663    def nom_datfi(self):
664        return "DAT_FI_VER"
665
666    @property
667    def nom_idx_datini(self):
668        return self.get_nom_obj_sql(self.nom_taula, self.prefix_index, self.sufix_taula_vers + "_" + self.nom_datini)
669
670    @property
671    def nom_idx_datfi(self):
672        return self.get_nom_obj_sql(self.nom_taula, self.prefix_index, self.sufix_taula_vers + "_" + self.nom_datfi)
673
674    @property
675    def nom_taula_vers_pk(self):
676        return self.get_nom_obj_sql(self.nom_taula, self.prefix_primary_key, self.sufix_taula_vers)
677
678    @property
679    def idx_nom_taula_vers_pk_datini(self):
680        return self.get_nom_obj_sql(self.nom_taula, "PKDAT_", self.sufix_taula_vers + self.sufix_unic)
681
682    @property
683    def nom_taula_vers_datini_chk(self):
684        return self.get_nom_obj_sql(self.nom_taula, "DAT_", self.sufix_taula_vers + self.sufix_check_constraint)
685
686    def get_triggers_extra_tab_base(self):
687        """
688        (SUBCLASEAR) Retorna lista strings con las definiciones de triggers extra a incluir
689        """
690        return []
691
692    @property
693    def triggers_extra_tab_base(self):
694        str_ret = ""
695        l_trgs = self.get_triggers_extra_tab_base()
696        if l_trgs:
697            str_ret = "{a_sql}".format(a_sql="\n".join(l_trgs))
698
699        return str_ret
700
701    def get_follows_trigger_ins_upd_tab_base(self):
702        """
703        (SUBCLASEAR) Retorna lista de strings con los nombres de triggers que precederán al trigger de la tabla
704        base que se encargará del versionado (el del nombre dado por property 'self.nom_nom_trigger_ins_upd_tab_base')
705        """
706        return []
707
708    @property
709    def follows_trigger_ins_upd_tab_base(self):
710        str_ret = ""
711        l_trgs = self.get_follows_trigger_ins_upd_tab_base()
712        if l_trgs:
713            str_ret = "\nFOLLOWS {a_sql}".format(a_sql=", ".join(l_trgs))
714
715        return str_ret
SCD4 = 'SCD4'
SCD4c = 'SCD4C'
SCD2 = 'SCD2'
prefix_val_param = 'A_'
sufix_taula_vers = '_VE'
sufix_seq = '_SEQ'
limit_noms_sql = 30
nom_prev_reg_vers = 'PREV_REG_VERS'
nom_new_reg_vers = 'NEW_REG_VERS'
nom_regaux_vers = 'REGAUX'
nom_old_reg_vers = 'OLD_REG'
prefix_delete_trigger = 'DEL_'
prefix_insert_trigger = 'INS_'
prefix_update_trigger = 'UPD_'
prefix_primary_key = 'PK_'
prefix_ins_upd_trigger = 'INS_UPD_'
prefix_index = 'IDX_'
sufix_unic = '_UN'
sufix_trigger = '_TRG'
sufix_fk = '_FK'
sufix_check_constraint = '_CHK'
prefix_check_constraint = 'CHK_'
default_date_version = 'CURRENT_DATE'
templates_tipo = {'SCD4': 'template_base_SCD4.sql', 'SCD4C': 'template_base_SCD4c.sql', 'SCD2': 'template_base_SCD2.sql'}
def convert_ddl_file_to_scd( self, ddl_file, list_extra_camps=None, a_sql_date_version=None, tipo_scd='SCD4'):
175    def convert_ddl_file_to_scd(self, ddl_file,
176                                list_extra_camps=None,
177                                a_sql_date_version=None,
178                                tipo_scd='SCD4'):
179        """
180        Genera a partir de DDL de tabla de Oracle (ddl_file) un nuevo script sobre el subdirectorio 'ddl_[tipo_scd]' con el mismo nombre pero el sufijo '_VE', con los objectos SQL (create, triggers, index, ...) que permitirán la gestión de versiones de cada cambio sobre dicha tabla.
181
182        Args:
183            ddl_file: path del fichero DDL de la tabla Oracle que se quiere versionar
184            list_extra_camps: (OPC) lista con definicion de campos sql
185            a_sql_date_version: (OPC) senetencia sql que devolverá la fecha de versión. Por defecto 'CURRENT_DATE'
186            tipo_scd: (OPC) el tipo de SCD que se quiere crear. Opciones disponibles: SCD4 y SCD4C. Por defecto 'SCD4' (Slowly changing dimension 4). El tipo SCD4C añade a la tabla de versiones compound triggers de Oracle para mantener integridad fechas sin incurrir en error 'mutating tables'
187
188        Returns:
189            path_file_scd (string)
190        """
191
192        ok = self._set_parser_ddl_file(ddl_file, list_extra_camps)
193        if ok:
194            self.sql_date_version = self.default_date_version
195            if a_sql_date_version:
196                self.sql_date_version = a_sql_date_version
197
198            return self.create_ddl_scd_file(ddl_file, tipo_scd)

Genera a partir de DDL de tabla de Oracle (ddl_file) un nuevo script sobre el subdirectorio 'ddl_[tipo_scd]' con el mismo nombre pero el sufijo '_VE', con los objectos SQL (create, triggers, index, ...) que permitirán la gestión de versiones de cada cambio sobre dicha tabla.

Arguments:
  • ddl_file: path del fichero DDL de la tabla Oracle que se quiere versionar
  • list_extra_camps: (OPC) lista con definicion de campos sql
  • a_sql_date_version: (OPC) senetencia sql que devolverá la fecha de versión. Por defecto 'CURRENT_DATE'
  • tipo_scd: (OPC) el tipo de SCD que se quiere crear. Opciones disponibles: SCD4 y SCD4C. Por defecto 'SCD4' (Slowly changing dimension 4). El tipo SCD4C añade a la tabla de versiones compound triggers de Oracle para mantener integridad fechas sin incurrir en error 'mutating tables'
Returns:

path_file_scd (string)

def create_ddl_scd_file(self, ddl_file, tipo_scd):
249    def create_ddl_scd_file(self, ddl_file, tipo_scd):
250        """
251        Crear fichero DDL SCD según tipo
252
253        Args:
254            ddl_file:
255            tipo_scd:
256
257        Returns:
258            ddl_file_scd (string): path file SCD
259        """
260        parts_file = os.path.splitext(os.path.basename(ddl_file))
261        dir_ver = os.path.join(os.path.dirname(ddl_file), "ddls_" + tipo_scd)
262        ddl_file_ver = os.path.join(dir_ver, parts_file[0] + "_VE" + parts_file[1])
263
264        if not os.path.exists(dir_ver):
265            os.makedirs(dir_ver)
266
267        mode_io = "w"
268        if not os.path.exists(ddl_file_ver):
269            mode_io = "x"
270
271        with open(ddl_file_ver, mode_io, encoding='utf-8') as a_file:
272            a_file.write(self.ddl_parser.as_script_sql)
273            a_file.write("\n")
274            a_file.write(self.get_ddl_scd(tipo_scd=tipo_scd))
275
276            # Se añade los sql_statements al DDL de la tabla versionada que no hagan referencia a 'ALTER TABLE tab ADD'
277            rex_alter_add = re.compile(r"ALTER TABLE [']?" + self.nom_taula + "[']? ADD", re.IGNORECASE)
278            rex_nom_tab_ve = re.compile(r"[\w'\"]*" + self.nom_taula_vers + r"[\w'\"]*", re.IGNORECASE)
279            for a_stmnt in self.ddl_parser.others_apb_statements():
280                if rex_alter_add.match(a_stmnt.format_val):
281                    continue
282
283                a_elem_pos_stmnt = a_stmnt.get_elem_match_val(rex_ini_pal + self.nom_taula + rex_fin_pal)
284                if a_elem_pos_stmnt:
285                    a_elem_pos_stmnt.elem.substitute_val(self.nom_taula, self.nom_taula_vers)
286
287                id_stmnt = a_stmnt.id_principal
288                if id_stmnt is not None and not rex_nom_tab_ve.match(id_stmnt):
289                    a_stmnt.substitute_val(id_stmnt,
290                                           self.get_nom_obj_sql(id_stmnt, sufix=self.sufix_taula_vers))
291
292                a_file.write("\n\n")
293                a_file.write(a_stmnt.format_val)
294                a_file.write("\n")
295                a_file.write("/")
296                a_file.write("\n")
297
298        return ddl_file_ver

Crear fichero DDL SCD según tipo

Arguments:
  • ddl_file:
  • tipo_scd:
Returns:

ddl_file_scd (string): path file SCD

def get_ddl_tmpl(self, tipo_scd='SCD4'):
300    def get_ddl_tmpl(self, tipo_scd=SCD4):
301        """
302        Retorna template a usar según tipo_scd
303
304        Args:
305            tipo_scd:
306
307        Returns:
308            template_scd (string): path del template a utilizar
309        """
310        a_template_scd = ""
311        tipo_scd = tipo_scd.upper()
312
313        if tipo_scd == self.SCD4c:
314            with open(os.path.join(os.path.dirname(__file__), self.templates_tipo[self.SCD4]),
315                      encoding='utf8') as a_file:
316                a_template_scd += a_file.read()
317                a_template_scd += "\n"
318
319        with open(os.path.join(os.path.dirname(__file__), self.templates_tipo[tipo_scd]),
320                  encoding='utf8') as a_file:
321            a_template_scd += a_file.read()
322
323        return a_template_scd

Retorna template a usar según tipo_scd

Arguments:
  • tipo_scd:
Returns:

template_scd (string): path del template a utilizar

def get_ddl_scd(self, tipo_scd='SCD4'):
325    def get_ddl_scd(self, tipo_scd=SCD4):
326        """
327        Retorna ddl SCD base
328        Args:
329            tipo_scd:
330
331        Returns:
332            a_ddl_scd (string): path ddl scd base
333        """
334        a_ddl_scd = self.get_ddl_tmpl(tipo_scd).format(self=self)
335
336        return a_ddl_scd

Retorna ddl SCD base

Arguments:
  • tipo_scd:
Returns:

a_ddl_scd (string): path ddl scd base

def add_property_func(self, nom_prop, a_func):
338    def add_property_func(self, nom_prop, a_func):
339        """
340        Añade funcion a self.__class__ para poder usar templates personalizados
341
342        Args:
343            nom_prop:
344            a_func:
345        """
346        setattr(self.__class__, nom_prop, property(a_func))

Añade funcion a self.__class__ para poder usar templates personalizados

Arguments:
  • nom_prop:
  • a_func:
@staticmethod
def format_nom_ddl(nom_ddl):
348    @staticmethod
349    def format_nom_ddl(nom_ddl):
350        """
351        Para cualquier string que reciba como nombre de elemento en DDL lo formatea a mayúsculas y sin "
352        Args:
353            nom_ddl:
354
355        Returns:
356            string
357        """
358        #
359        return nom_ddl.strip().replace('"', '').upper()

Para cualquier string que reciba como nombre de elemento en DDL lo formatea a mayúsculas y sin "

Arguments:
  • nom_ddl:
Returns:

string

def clau_as_def_params_func(self, prefix_param_templ='A_', def_param=False):
361    def clau_as_def_params_func(self, prefix_param_templ=prefix_val_param, def_param=False):
362        """
363        Retorna clave tabla como parametros de funcion de PL/SQL
364        Args:
365            prefix_param_templ:
366            def_param:
367
368        Returns:
369            string
370        """
371        sufix_param_templ = ""
372        if def_param:
373            sufix_param_templ = " " + self.nom_taula_vers + ".{nom_camp_clau}%TYPE"
374
375        param_templ = prefix_param_templ + "{nom_camp_clau}" + sufix_param_templ
376        claus_as_params = []
377        for nom_camp in self.nom_camps_clau:
378            claus_as_params.append(param_templ.format(nom_camp_clau=nom_camp))
379
380        return ", ".join(claus_as_params)

Retorna clave tabla como parametros de funcion de PL/SQL

Arguments:
  • prefix_param_templ:
  • def_param:
Returns:

string

def sql_query_eq_clau(self, prefix_param='A_'):
382    def sql_query_eq_clau(self, prefix_param=prefix_val_param):
383        """
384        Retorna clave tabla como query SQL
385        Args:
386            prefix_param:
387
388        Returns:
389            string
390        """
391        prefix_param_templ = ""
392        if prefix_param is not None:
393            prefix_param_templ = prefix_param
394
395        query_eq_clau_templ = self.nom_taula_vers + ".{nom_clau} = " + prefix_param_templ + "{nom_clau}"
396        list_query_claus = []
397        for nom_camp in self.nom_camps_clau:
398            list_query_claus.append(query_eq_clau_templ.format(nom_clau=nom_camp))
399
400        return " AND ".join(list_query_claus)

Retorna clave tabla como query SQL

Arguments:
  • prefix_param:
Returns:

string

def def_set_camps_clau(self, oper_set='=', sep_set_camp=', ', prefix_camp='', prefix_val=''):
402    def def_set_camps_clau(self,
403                           oper_set="=",
404                           sep_set_camp=", ",
405                           prefix_camp="",
406                           prefix_val=""):
407        """
408        Retorna SQL para hacer SET de valores de los campos clave
409        Args:
410            oper_set:
411            sep_set_camp:
412            prefix_camp:
413            prefix_val:
414
415        Returns:
416            string
417        """
418        if len(prefix_camp) != 0:
419            prefix_camp += "."
420        if len(prefix_val) != 0:
421            prefix_val += "."
422
423        set_camp_templ = prefix_camp + "{nom_camp} " + oper_set + " " + prefix_val + "{nom_camp}"
424
425        list_set_camps = []
426        for a_nom_camp in self.nom_camps_clau:
427            list_set_camps.append(set_camp_templ.format(nom_camp=a_nom_camp))
428
429        return sep_set_camp.join(list_set_camps)

Retorna SQL para hacer SET de valores de los campos clave

Arguments:
  • oper_set:
  • sep_set_camp:
  • prefix_camp:
  • prefix_val:
Returns:

string

def def_set_camps_taula( self, oper_set='=', sep_set_camp=', ', prefix_camp='', prefix_val='', set_camps_clau=False):
431    def def_set_camps_taula(self,
432                            oper_set="=",
433                            sep_set_camp=", ",
434                            prefix_camp="",
435                            prefix_val="",
436                            set_camps_clau=False):
437        """
438        Retorna SQL para hacer SET de los valores de los campos
439        Args:
440            oper_set:
441            sep_set_camp:
442            prefix_camp:
443            prefix_val:
444            set_camps_clau:
445
446        Returns:
447            string
448        """
449        if len(prefix_camp) != 0:
450            prefix_camp += "."
451        if len(prefix_val) != 0:
452            prefix_val += "."
453
454        set_camp_templ = prefix_camp + "{nom_camp} " + oper_set + " " + prefix_val + "{nom_camp}"
455
456        list_set_camps = []
457        for a_nom_camp in self.def_camps_taula.keys():
458            if not set_camps_clau and a_nom_camp in self.nom_camps_clau:
459                continue
460
461            list_set_camps.append(set_camp_templ.format(nom_camp=a_nom_camp))
462
463        return sep_set_camp.join(list_set_camps)

Retorna SQL para hacer SET de los valores de los campos

Arguments:
  • oper_set:
  • sep_set_camp:
  • prefix_camp:
  • prefix_val:
  • set_camps_clau:
Returns:

string

def get_nom_obj_sql(self, nom_base, prefix='', sufix=''):
465    def get_nom_obj_sql(self, nom_base, prefix="", sufix=""):
466        """
467        Devuelve nombre objecto SQL con prefijo y sufijo ajustado a la longitud máxima de nombres en PL/SQL
468        Args:
469            nom_base:
470            prefix:
471            sufix:
472
473        Returns:
474            string
475        """
476        return SqlParser.get_nom_obj_sql(nom_base, prefix, sufix)

Devuelve nombre objecto SQL con prefijo y sufijo ajustado a la longitud máxima de nombres en PL/SQL

Arguments:
  • nom_base:
  • prefix:
  • sufix:
Returns:

string

date_version
478    @property
479    def date_version(self):
480        return self.sql_date_version
def camps_clau(self):
482    def camps_clau(self):
483        for nom_camp in self.nom_camps_clau:
484            yield "{} {}".format(nom_camp, self.def_camps_taula.get(nom_camp))
def_camps_clau
486    @property
487    def def_camps_clau(self):
488        return ", ".join(self.camps_clau())
def camps_dades(self):
490    def camps_dades(self):
491        for nom_camp in sorted(self.def_camps_taula):
492            if nom_camp not in self.nom_camps_clau:
493                yield "{} {}".format(nom_camp, self.def_camps_taula.get(nom_camp))
def_camps_dades
495    @property
496    def def_camps_dades(self):
497        return ", ".join(self.camps_dades())
alter_camps_dades_taula_orig
499    @property
500    def alter_camps_dades_taula_orig(self):
501        return "\n".join(["ALTER TABLE {nom_taula} ADD ({def_col});".format(nom_taula=self.nom_taula,
502                                                                            def_col=def_col)
503                          for def_col in self.camps_dades()])
alter_camps_dades_taula_vers
505    @property
506    def alter_camps_dades_taula_vers(self):
507        return "\n".join(["ALTER TABLE {nom_taula} ADD ({def_col});".format(nom_taula=self.nom_taula_vers,
508                                                                            def_col=def_col)
509                          for def_col in self.camps_dades()])
str_camps_clau
511    @property
512    def str_camps_clau(self):
513        return ", ".join(self.nom_camps_clau)
primer_camp_clau
515    @property
516    def primer_camp_clau(self):
517        return self.nom_camps_clau[0]
nom_taula_vers
519    @property
520    def nom_taula_vers(self):
521        return self.get_nom_obj_sql(self.nom_taula, sufix=self.sufix_taula_vers)
nom_seq_vers
523    @property
524    def nom_seq_vers(self):
525        return self.get_nom_obj_sql(self.nom_taula, sufix=self.sufix_taula_vers + self.sufix_seq)
def_cursor_prev_date_reg_vers
527    @property
528    def def_cursor_prev_date_reg_vers(self):
529        def_curs_templ = "({params_clau}, A_DAT_VER DATE) IS " \
530                         "SELECT * FROM " + self.nom_taula_vers + \
531                         " WHERE {query_clau} AND " + \
532                         self.nom_taula_vers + "." + self.nom_datini + \
533                         " <= A_DAT_VER ORDER BY " + self.nom_datini + " DESC"
534
535        return def_curs_templ.format(params_clau=self.clau_as_def_params_func(def_param=True),
536                                     query_clau=self.sql_query_eq_clau())
def_cursor_actual_reg_vers
538    @property
539    def def_cursor_actual_reg_vers(self):
540        def_curs_templ = "({params_clau}) IS " \
541                         "SELECT * FROM " + self.nom_taula_vers + \
542                         " WHERE {query_clau} AND " + \
543                         self.nom_taula_vers + "." + self.nom_datfi + \
544                         " IS NULL ORDER BY " + self.nom_datini + " DESC"
545
546        return def_curs_templ.format(params_clau=self.clau_as_def_params_func(def_param=True),
547                                     query_clau=self.sql_query_eq_clau())
params_cursor_clau_reg_new
549    @property
550    def params_cursor_clau_reg_new(self):
551        return self.clau_as_def_params_func(prefix_param_templ=":NEW.")
params_cursor_clau_reg_old
553    @property
554    def params_cursor_clau_reg_old(self):
555        return self.clau_as_def_params_func(prefix_param_templ=":OLD.")
sql_query_eq_clau_for_regaux
557    @property
558    def sql_query_eq_clau_for_regaux(self):
559        return self.sql_query_eq_clau(prefix_param=(self.nom_regaux_vers + "."))
sql_query_eq_clau_for_new_reg
561    @property
562    def sql_query_eq_clau_for_new_reg(self):
563        return self.sql_query_eq_clau(prefix_param=":NEW.")
sql_query_eq_clau_for_prev_reg
565    @property
566    def sql_query_eq_clau_for_prev_reg(self):
567        return self.sql_query_eq_clau(prefix_param=(self.nom_prev_reg_vers + "."))
sql_query_eq_clau_for_old_reg
569    @property
570    def sql_query_eq_clau_for_old_reg(self):
571        return self.sql_query_eq_clau(prefix_param=(self.nom_old_reg_vers + "."))
set_camps_new_reg_ver
573    @property
574    def set_camps_new_reg_ver(self):
575        return self.def_set_camps_taula(oper_set=":=",
576                                        sep_set_camp=";\n",
577                                        prefix_camp=self.nom_new_reg_vers,
578                                        prefix_val=":NEW",
579                                        set_camps_clau=True)
set_camps_update_reg_ver
581    @property
582    def set_camps_update_reg_ver(self):
583        return self.def_set_camps_taula(prefix_val=":NEW")
nom_trigger_del_tab_base
585    @property
586    def nom_trigger_del_tab_base(self):
587        return self.get_nom_obj_sql(self.nom_taula,
588                                    self.prefix_delete_trigger,
589                                    self.sufix_trigger)
nom_trigger_ins_upd_tab_base
591    @property
592    def nom_trigger_ins_upd_tab_base(self):
593        return self.get_nom_obj_sql(self.nom_taula,
594                                    self.prefix_ins_upd_trigger,
595                                    self.sufix_trigger)
nom_trigger_del_tab_vers
597    @property
598    def nom_trigger_del_tab_vers(self):
599        return self.get_nom_obj_sql(self.nom_taula,
600                                    self.prefix_delete_trigger,
601                                    self.sufix_taula_vers + self.sufix_trigger)
nom_trigger_ins_tab_vers
603    @property
604    def nom_trigger_ins_tab_vers(self):
605        return self.get_nom_obj_sql(self.nom_taula,
606                                    self.prefix_insert_trigger,
607                                    self.sufix_taula_vers + self.sufix_trigger)
nom_trigger_upd_tab_vers
609    @property
610    def nom_trigger_upd_tab_vers(self):
611        return self.get_nom_obj_sql(self.nom_taula,
612                                    self.prefix_update_trigger,
613                                    self.sufix_taula_vers + self.sufix_trigger)
nom_constraint_chk_vers
615    @property
616    def nom_constraint_chk_vers(self):
617        return self.get_nom_obj_sql(self.nom_taula,
618                                    sufix=self.sufix_check_constraint)
nom_var_prev_reg_vers
620    @property
621    def nom_var_prev_reg_vers(self):
622        return self.nom_prev_reg_vers
nom_var_new_reg_vers
624    @property
625    def nom_var_new_reg_vers(self):
626        return self.nom_new_reg_vers
nom_var_regaux_vers
628    @property
629    def nom_var_regaux_vers(self):
630        return self.nom_regaux_vers
set_camps_clau_regaux_from_new
632    @property
633    def set_camps_clau_regaux_from_new(self):
634        return self.def_set_camps_clau(oper_set=":=",
635                                       sep_set_camp=";",
636                                       prefix_camp=self.nom_regaux_vers,
637                                       prefix_val=":NEW")
set_camps_clau_regaux_from_old
639    @property
640    def set_camps_clau_regaux_from_old(self):
641        return self.def_set_camps_clau(oper_set=":=",
642                                       sep_set_camp=";",
643                                       prefix_camp=self.nom_regaux_vers,
644                                       prefix_val=":OLD")
nom_var_old_reg_vers
646    @property
647    def nom_var_old_reg_vers(self):
648        return self.nom_old_reg_vers
set_camps_oldreg_from_old
650    @property
651    def set_camps_oldreg_from_old(self):
652        return self.def_set_camps_taula(oper_set=":=",
653                                        sep_set_camp=";\n",
654                                        prefix_camp=self.nom_old_reg_vers,
655                                        prefix_val=":OLD",
656                                        set_camps_clau=True)
nom_datini
658    @property
659    def nom_datini(self):
660        return "DAT_INI_VER"
nom_datfi
662    @property
663    def nom_datfi(self):
664        return "DAT_FI_VER"
nom_idx_datini
666    @property
667    def nom_idx_datini(self):
668        return self.get_nom_obj_sql(self.nom_taula, self.prefix_index, self.sufix_taula_vers + "_" + self.nom_datini)
nom_idx_datfi
670    @property
671    def nom_idx_datfi(self):
672        return self.get_nom_obj_sql(self.nom_taula, self.prefix_index, self.sufix_taula_vers + "_" + self.nom_datfi)
nom_taula_vers_pk
674    @property
675    def nom_taula_vers_pk(self):
676        return self.get_nom_obj_sql(self.nom_taula, self.prefix_primary_key, self.sufix_taula_vers)
idx_nom_taula_vers_pk_datini
678    @property
679    def idx_nom_taula_vers_pk_datini(self):
680        return self.get_nom_obj_sql(self.nom_taula, "PKDAT_", self.sufix_taula_vers + self.sufix_unic)
nom_taula_vers_datini_chk
682    @property
683    def nom_taula_vers_datini_chk(self):
684        return self.get_nom_obj_sql(self.nom_taula, "DAT_", self.sufix_taula_vers + self.sufix_check_constraint)
def get_triggers_extra_tab_base(self):
686    def get_triggers_extra_tab_base(self):
687        """
688        (SUBCLASEAR) Retorna lista strings con las definiciones de triggers extra a incluir
689        """
690        return []

(SUBCLASEAR) Retorna lista strings con las definiciones de triggers extra a incluir

triggers_extra_tab_base
692    @property
693    def triggers_extra_tab_base(self):
694        str_ret = ""
695        l_trgs = self.get_triggers_extra_tab_base()
696        if l_trgs:
697            str_ret = "{a_sql}".format(a_sql="\n".join(l_trgs))
698
699        return str_ret
def get_follows_trigger_ins_upd_tab_base(self):
701    def get_follows_trigger_ins_upd_tab_base(self):
702        """
703        (SUBCLASEAR) Retorna lista de strings con los nombres de triggers que precederán al trigger de la tabla
704        base que se encargará del versionado (el del nombre dado por property 'self.nom_nom_trigger_ins_upd_tab_base')
705        """
706        return []

(SUBCLASEAR) Retorna lista de strings con los nombres de triggers que precederán al trigger de la tabla base que se encargará del versionado (el del nombre dado por property 'self.nom_nom_trigger_ins_upd_tab_base')

follows_trigger_ins_upd_tab_base
708    @property
709    def follows_trigger_ins_upd_tab_base(self):
710        str_ret = ""
711        l_trgs = self.get_follows_trigger_ins_upd_tab_base()
712        if l_trgs:
713            str_ret = "\nFOLLOWS {a_sql}".format(a_sql=", ".join(l_trgs))
714
715        return str_ret
ddl_parser
def_camps_taula
nom_camps_clau
nom_taula
sql_date_version