 create table pt_config
 (owner varchar2(32),
 table_name varchar2(32),
 tablespace_name varchar2(32),
 date_type varchar2(8),
 force_split varchar2(1),
  force_merge varchar2(1),
  force_drop  varchar2(1),
  drop_physical varchar2(1),
  intervall_adv number);
  
 alter table  pt_config
 add constraint pt_config_pk
 primary key (owner,table_name);
  
  
  alter table  pt_config
 add constraint chk_date_type
 check (date_type in ('WEEK','MONTH','DAY','YEAR'));
 
 
 
CREATE OR REPLACE PACKAGE psm_pt
 AS
	-- psm_pt
	-- 2009; Ingo Voland
	--
	-- change hist:
	-- 08/2009 (IV)  Partition-Merging in Monatspartitionen hinzugefügt
	-- 				  splitting einer maxvalue partition beim erstellen neuen partitionen hinzugefügt
	--
	-- 08/2009 (IV)  Partitions auf Jahresbasis hinzugefügt
	--
	--03/2010 (IV) added Schema support
	--04/2010 (IV) added truncate single partition support
	--10/2010 (IV) added partition verification functions
	-- 				added support for configuration tables into crpt
	--
	--
	--
	-- Package enthaelt Funktionalitaeten fuer Partitionsmanagement
	-- benoetigt: Date oder CHAR(datebasierend) Range-Partitionierte Tabellen
	--
	-- Übersicht:
	--  Das Pakage dieht dem Partitionsmanagement. Es legt Partitionen basierend auf
	--  dem in Aufruf übergebenen Datum für die jeweils angegebene Tabelle an
	--  Ebenfalls können Paritionen über truncate für einen bestimmten historischen
	--  zeitraum geleert oder per drop gelöscht werden.
	--  Die Proceduren um truncate /drop warnen, falls es global Indexe gibt, die bei
	--  bei einer solchen Operation invalidiert werden.
	--
	--  Die Range-Partitioniert Tabelle kann sowohl nach einer Splate vom type Date7Timestamp
	--  als auch nach einer Spalte vom Type char/varchar2 partitioniert sein.
	--
	--  die Partitionsnamen und Werte werden wie folgt vergeben_
	--
	--  Partitioniertungszeiträume / Namen bei einer DATE-Type partitionierten Tabelle
	--
	--   Zeitraum	 Partitionsname					Partitionsvalue
	-- 	Monat 	Pyyyymm	->  (P200907)	 to_date(last_day(part_date) + 1)  ->	 to_date('20090801','yyyymmdd')
	-- 	Woche 	Pyyyy_ww  -> (P2009_35)  to_date(first_day_of_next_week(part_date))	-> to_date('20090907','yyyymmdd')
	-- 	Tag		Pyyyy_mm_dd -> (P2009_09_07)	to_date(part_date + 1)	-> to_date('20090908,'yyyymmdd')
	--
	--
	--  Partitioniertungszeiträume / Namen bei einer CHAR-Type partitionierten Tabelle
	--
	--   Zeitraum	 Partitionsname					Partitionsvalue
	-- 	Monat 	Pyyyymm	->  (P200908)	 to_char(last_day(part_date) )||'00'  ->   '20090900'
	-- 	Woche 	Pyyyymmdd  -> (P20100222)	to_char(first_day_of_next_week(part_date))  -> '20100222'
	-- 	Tag		Pyyyymmdd -> (P20100310)  to_char(part_date + 1)  -> '20100311'



	-- crpt
	-- Procedure zum Erstellen neuer Partitionen
	--   p_table_name -> Tabellen namen
	--   p_date_type -> Partitioniertungszeiträume, gültige Werte sind 'DAY','WEEK' , 'MONTH'
	--   p_day Max_Value für die Partition ( less than ...)
	--
	--   Sollte eine Tabelle über eine MAXVALUE partition verfügen wird geprüft,
	--   ob die neue partition über einen split der MXVALUE partition erstellt werden kann.
	--   global + local indexes werden über index rebuild  neu aufgebaut. Sollten globale indexe
	--   vorhanden sein kann dies zu einer erheblichen laufzeit führen
	--
	--  Beispiel:
	--   psm_pt.crpt('MY_TABLE','WEEK',sysdate)
	--   psm_pt.crpt('MY_TABLE','DAY',sysdate + 5 )
	--   psm_pt.crpt('MY_TABLE','MONTH',to_date('20090701'))
	--

	PROCEDURE crpt (
		p_table_name			VARCHAR2,
		p_date_type 			VARCHAR2 := 'WEEK',
		p_day 					DATE := SYSDATE,
		p_force					BOOLEAN DEFAULT FALSE,
		p_tablespace_name 	user_tab_partitions.tablespace_name%TYPE DEFAULT NULL
	);

	--
	-- hier wird die konfiguratio aus der Tabelle pt_config genommen
	--
	PROCEDURE crpt;

	-- clean_partitions
	-- Procedure zum Löschen von Partitionen
	--   p_table_name -> Tabellen Name
	--   p_max_date -> Datum, bis zu dem die Partitionen per drop oder truncate gelöscht werden
	--   force_drop -> die Procedure prüft auf das Vorhandensein globaler Indexe. Falls es diese
	-- 					 gibt wird eine Fehlermeldung gegeben. Wenn force_drop = TRUE
	-- 					 wird die Partition dennoch gelöscht bzw geleert, im Anschluß
	-- 					 muß der use aber den globalen index neu aufbauen
	-- 					 Der Default-Wert ist FALSE
	--  drop_physicaly -> True führt ein DROP Partition aus, False führt ein Truncate Partition aus
	--
	--  Beispiel:
	--   psm_pt.clean_partitions('MY_TABLE',sysdate - 50, false, true);
	--

	PROCEDURE clean_partitions (
		p_table_name		  VARCHAR2,
		p_max_date			  DATE DEFAULT SYSDATE - 180,
		force_drop		  IN BOOLEAN DEFAULT FALSE,
		drop_physicaly   IN BOOLEAN DEFAULT FALSE
	);

	-- merge_partitions
	-- Procedure zum Mergen von Partitionen. hierbei werden Tage oder Wochenpartitionen zu Monatspartitionen
	-- zuammen gefügt. Die Procedure führt ein rebuild der betroffenen lokalen Indexe aus
	-- Vorhandene globale Indexe führen, falls nicht 'force_merge=true' gesetzt ist, zu Fehlermeldungen.
	-- Wenn mit force_merge gearbeitet wird müssen eventuelle globale indexe neu validiert werden
	--
	-- Es ist empfehlenswert, das max-date immer der letzte Tage eines betroffenen Monats ist. In diesem Fall
	-- erfolgt auch einen Umbenennung der Partition in das Monats-Partitionen-Namensschema
	--
	--   p_table_name -> Tabellen Name
	--   p_min_date -> Datum, ab dem die Partitionen per merge zuammen geführt werden (unteres Limit)
	--   p_max_date -> Datum, bis zu dem die Partitionen per merge zuammen geführt werden (oberes Limit)
	--   force_merge -> die Procedure prüft auf das Vorhandensein globaler Indexe. Falls es diese
	-- 					 gibt wird eine Fehlermeldung gegeben. Wenn force_merge = TRUE
	-- 					 wird die Partition dennoch gelöscht bzw geleert, im Anschluß
	-- 					 muß der use aber den globalen index neu aufbauen
	-- 					 Der Default-Wert ist FALSE
	--
	--  Beispiel:
	--   psm_pt.merge_partitions('MY_TABLE',sysdate - 80, last_day(sysdate -30), false, true);
	--

	PROCEDURE clean_partitions (
		p_table_name		VARCHAR2,
		p_part				VARCHAR2,
		force_drop		IN BOOLEAN DEFAULT FALSE
	);

	-- clean PArtition ist overloaded und kann auch mit einer einzelnen partition verwendet werden

	PROCEDURE merge_partitions (
		p_table_name		VARCHAR2,
		p_min_date			DATE DEFAULT SYSDATE - 180,
		p_max_date			DATE DEFAULT SYSDATE - 60,
		force_merge 	IN BOOLEAN DEFAULT FALSE
	);

	--
	-- Intern genutzte definitionen
	--
	PROCEDURE truncate_sub_part (
		p_table_name		VARCHAR2,
		p_sub_part			VARCHAR2,
		force_drop		IN BOOLEAN DEFAULT FALSE
	);

	PROCEDURE drop_part (
		p_table_name		VARCHAR2,
		p_part				VARCHAR2,
		force_drop		IN BOOLEAN DEFAULT FALSE
	);



	--
	-- Funktionen zum Prüfen der Verfügbarkeit der Partitionen
	--  Es wird geprüft, ob eine Partition für das Datum p_min_required_date in der Tabelle enthalten ist.
	--  Mögliche Rückgabewerte sind :
	--
	--  OK: partition Pxxxxxx for user.table exisiert
	--  Warn: Maxvalue for user.table exisiert
	--  ERROR: Partition missing for user.table
	--
	--  Im Falle einer fehlenden Parition wird ein Eintrag in den Oracle Alert Log geschrieben.
	--
	-- Verwendungsbeispiele:
	-- 	 select * from table (psm_pt.f_check_partitions('USERNAME.TABLE_NAME',sysdate +10))
	-- 	select * from table (psm_pt.f_check_partitions(sysdate +100)
	--
	-- Wird kein Tabellen Name explizit vergeben werden alle über all_tables gefundenen
	-- partitionierten Tabellen geprüft
	--



	FUNCTION f_check_partitions (
		p_table_name			 IN VARCHAR2,
		p_min_required_date	 IN DATE DEFAULT SYSDATE + 7
	)
		RETURN xdb.xdb$string_list_t
		PIPELINED;

	FUNCTION f_check_partitions (
		p_min_required_date IN DATE DEFAULT SYSDATE + 7
	)
		RETURN xdb.xdb$string_list_t
		PIPELINED;


	FUNCTION show_version
		RETURN VARCHAR2;
--
-- known issues
--   (1) execute	sys.dbms_system.ksdwrt muss verfügbar sein
--   (2) wenn es mit authid current user kompiliert wird muss der jeweils ausfuehrende user alle notwendigen Rechte
-- 		(alter table) an den betroffenen Tabellen haben
-- 		erfolgt die Komilierung ohne	authid current user müssen die rechte an alle zu betrachtenden tabellen
-- 		an den package owner gegranted werden
END psm_pt;
/


CREATE OR REPLACE PACKAGE BODY psm_pt AS
	c_version			 CONSTANT VARCHAR2 (32) := '03.10 / 20101030';
	v_stmt							 VARCHAR2 (1000);
	char_partitioned				 BOOLEAN;
	p_lst_day_ident				 VARCHAR2 (2) := '00';
	c_first_day 		 CONSTANT DATE := TO_DATE ('00010101', 'yyyymmdd');

	e_invalid_input				 EXCEPTION;

	b_check_partition_success	 BOOLEAN DEFAULT FALSE;

	TYPE t_part_merge IS TABLE OF user_tab_partitions.partition_name%TYPE
									INDEX BY PLS_INTEGER;

	my_part_merge					 t_part_merge;

	TYPE t_tab_ident IS RECORD
	(
		owner 		 all_tables.owner%TYPE,
		table_name	 all_tables.table_name%TYPE
	);

	my_tab_ident					 t_tab_ident;


	CURSOR c_tab_partitions (
		owner 				  VARCHAR2,
		tab_name 			  VARCHAR2,
		p_partition_name	  VARCHAR2
	) IS
		SELECT	COUNT (*)
		  FROM	all_tab_partitions
		 WHERE		 table_name = UPPER (tab_name)
					AND table_owner = UPPER (owner)
					AND partition_name = UPPER (p_partition_name);

	--
	--

	PROCEDURE set_tab_ident (v_tab_ident IN VARCHAR2) IS
	BEGIN
		my_tab_ident.owner :=
			UPPER (
				NVL (
					SUBSTR (
						TRIM (v_tab_ident),
						1,
						INSTR (TRIM (v_tab_ident), '.') - 1
					),
					USER
				)
			);
		my_tab_ident.table_name :=
			UPPER (
				SUBSTR (
					TRIM (v_tab_ident),
					INSTR (TRIM (v_tab_ident), '.') + 1,
					100
				)
			);
		pkg_common_logging.write_log (
			'table owner: ' || my_tab_ident.owner,
			pkg_common_logging.set_debug
		);
		pkg_common_logging.write_log (
			'table_name: ' || my_tab_ident.table_name,
			pkg_common_logging.set_debug
		);
	END;

	PROCEDURE write_alert_log (
		v_error_code	IN NUMBER,
		v_error_msg 	IN VARCHAR2
	) IS
		v_line	VARCHAR2 (1000);
	BEGIN
		v_line :=
			TO_CHAR (
				SYSDATE,
				'Dy Mon dd hh24:mi:ss yyyy',
				'NLS_DATE_LANGUAGE = AMERICAN'
			)
			|| CHR (10)
			|| 'ORA'
			|| v_error_code
			|| ' warning: '
			|| v_error_msg;

		sys.DBMS_SYSTEM.ksdwrt (2, v_line);
		sys.DBMS_SYSTEM.ksdwrt (2, 'Partition split/extenesion is needed');
	END;



	FUNCTION check_global_index (p_table_name VARCHAR2, force_drop IN BOOLEAN)
		RETURN BOOLEAN IS
		e_global_index   EXCEPTION;
		v_id				  NUMBER;
	BEGIN
		-- check global indexes
		SELECT	COUNT (*)
		  INTO	v_id
		  FROM	all_indexes
		 WHERE		 partitioned <> 'YES'
					AND table_name = my_tab_ident.table_name
					AND index_type <> 'LOB'
					AND owner = my_tab_ident.owner;

		pkg_common_logging.write_log (
			'global indexes found: ' || TO_CHAR (v_id),
			pkg_common_logging.set_debug
		);

		-- bail out if global index exists
		IF (v_id > 0 AND NOT force_drop) THEN
			pkg_common_logging.write_log (
				'force_drop must be true in oder to modify partitions',
				pkg_common_logging.set_info
			);
			RAISE e_global_index;
		END IF;

		RETURN TRUE;
	EXCEPTION
		WHEN e_global_index THEN
			DBMS_OUTPUT.put_line (
				'Global Indexes exists, can not drop a partition without index invalidation'
			);
			RETURN FALSE;
		WHEN OTHERS THEN
			RAISE;
	END check_global_index;



	FUNCTION part_column_type (p_table_name IN VARCHAR2)
		RETURN VARCHAR2 IS
		v_ret   VARCHAR2 (32);
	BEGIN
		pkg_common_logging.set_client_info ('part_column_type');

		v_stmt :=
				'select b.data_type from '
			|| 'ALL_PART_KEY_COLUMNS a, '
			|| 'all_tab_columns b '
			|| 'where object_type = :b1 and '
			|| 'a.name = b.table_name and '
			|| 'a.column_name = b.column_name and '
			|| 'a.owner = b.owner and a.owner = :b2 and '
			|| 'a.name = :b3';
		pkg_common_logging.write_log (v_stmt, pkg_common_logging.set_debug);

		EXECUTE IMMEDIATE v_stmt
			INTO v_ret
			USING 'TABLE', my_tab_ident.owner, my_tab_ident.table_name;

		pkg_common_logging.write_log (
			'Return: ' || v_ret,
			pkg_common_logging.set_debug
		);

		RETURN v_ret;
	EXCEPTION
		WHEN NO_DATA_FOUND THEN
			pkg_common_logging.write_log (
				'Table not found!' || SQLERRM,
				pkg_common_logging.set_info
			);
			RAISE;
		WHEN OTHERS THEN
			pkg_common_logging.write_log (
				'psm_pt.part_column_type error:' || SQLERRM,
				pkg_common_logging.set_info
			);
			RAISE;
	END part_column_type;


	-------------------------------------------------------------------------------
	-- show partition name
	-------------------------------------------------------------------------------
	FUNCTION sh_partition_name (
		p_day 							DATE,
		p_date_type 					VARCHAR2 := 'DAY',
		p_partition_name_prefix 	VARCHAR2 := 'P'
	)
		RETURN VARCHAR2 IS
		l_holiday_days   INTEGER;
		v_ret 			  VARCHAR2 (32);
	BEGIN
		pkg_common_logging.set_client_info ('sh_partition_name');


		IF char_partitioned THEN
			v_ret :=
				CASE UPPER (p_date_type)
					WHEN 'DAY' THEN
						p_partition_name_prefix
						|| TO_CHAR (
								p_day,
								'yyyymmdd',
								'NLS_DATE_LANGUAGE = German'
							)
					WHEN 'MONTH' THEN
						p_partition_name_prefix
						|| TO_CHAR (p_day, 'yyyymm', 'NLS_DATE_LANGUAGE = German')
					WHEN 'WEEK' THEN
						p_partition_name_prefix
						|| TO_CHAR (
								TRUNC (p_day, 'DAY') + 7,
								'yyyymmdd',
								'NLS_DATE_LANGUAGE = German'
							)
					WHEN 'YEAR' THEN
						p_partition_name_prefix
						|| TO_CHAR (
								TRUNC (p_day, 'yyyy'),
								'yyyy',
								'NLS_DATE_LANGUAGE = German'
							)
				END;
		ELSE
			v_ret :=
				CASE UPPER (p_date_type)
					WHEN 'DAY' THEN
						p_partition_name_prefix
						|| TO_CHAR (
								p_day,
								'yyyy_mm_dd',
								'NLS_DATE_LANGUAGE = German'
							)
					WHEN 'MONTH' THEN
						p_partition_name_prefix
						|| UPPER (
								TO_CHAR (
									p_day,
									'yyyymm',
									'NLS_DATE_LANGUAGE = German'
								)
							)
					WHEN 'WEEK' THEN
						p_partition_name_prefix
						|| UPPER (
								TO_CHAR (
									p_day,
									'yyyy_ww',
									'NLS_DATE_LANGUAGE = German'
								)
							)
					WHEN 'YEAR' THEN
						p_partition_name_prefix
						|| TO_CHAR (
								TRUNC (p_day, 'yyyy'),
								'yyyy',
								'NLS_DATE_LANGUAGE = German'
							)
				END;
		END IF;

		pkg_common_logging.write_log (
			'sh_partition_name Return: ' || v_ret,
			pkg_common_logging.set_debug
		);

		RETURN v_ret;
	EXCEPTION
		WHEN OTHERS THEN
			RAISE;
	END sh_partition_name;

	--
	--
	-------------------------------------------------------------------------------
	-- show partition name
	-------------------------------------------------------------------------------
	FUNCTION sh_partitioning_key_value (
		p_day 			DATE,
		p_date_type 	VARCHAR2 := 'WEEK'
	)
		RETURN VARCHAR2 IS
		v_ret   VARCHAR2 (128);
	BEGIN
		pkg_common_logging.set_client_info ('sh_partitioning_key_value');


		IF char_partitioned THEN
			v_ret :=
				CASE UPPER (p_date_type)
					WHEN 'DAY' THEN
						'''' || TO_CHAR (p_day + 1, 'yyyymmdd') || ''''
					WHEN 'MONTH' THEN
							''''
						|| TO_CHAR (ADD_MONTHS (p_day, 1), 'yyyymm')
						|| p_lst_day_ident
						|| ''''
					WHEN 'WEEK' THEN
							''''
						|| TO_CHAR (TRUNC (p_day, 'DAY') + 7, 'yyyymmdd')
						|| ''''
					WHEN 'YEAR' THEN
						''''
						|| TO_CHAR (
								TRUNC (ADD_MONTHS (p_day, 12), 'yyyy'),
								'yyyymmdd'
							)
						|| ''''
				END;
		ELSE
			v_ret :=
				CASE UPPER (p_date_type)
					WHEN 'DAY' THEN
							'to_date ('
						|| ''''
						|| TO_CHAR (p_day + 1, 'yyyy_mm_dd')
						|| ''''
						|| ',''yyyy_mm_dd'')'
					WHEN 'MONTH' THEN
						'to_date (' || ''''
						|| TO_CHAR (
								TRUNC (ADD_MONTHS (p_day, 1), 'mm'),
								'yyyy_mm_dd'
							)
						|| ''''
						|| ',''yyyy_mm_dd'')'
					WHEN 'WEEK' THEN
							'to_date ('
						|| ''''
						|| TO_CHAR (TRUNC (p_day, 'DAY') + 7, 'yyyy_mm_dd')
						|| ''''
						|| ',''yyyy_mm_dd'')'
					WHEN 'YEAR' THEN
						'to_date (' || ''''
						|| TO_CHAR (
								TRUNC (ADD_MONTHS (p_day, 12), 'yyyy'),
								'yyyy_mm_dd'
							)
						|| ''''
						|| ',''yyyy_mm_dd'')'
				END;
		END IF;

		pkg_common_logging.write_log (
			'sh_partitioning_key_value Return: ' || v_ret,
			pkg_common_logging.set_debug
		);

		RETURN v_ret;
	EXCEPTION
		WHEN OTHERS THEN
			RAISE;
	END sh_partitioning_key_value;

	--
	--
	-------------------------------------------------------------------------------
	-- partition exists
	-------------------------------------------------------------------------------
	FUNCTION partition_exist (
		p_table_name		  VARCHAR2,
		p_partition_name	  VARCHAR2
	)
		--RETURN 		BOOLEAN
		RETURN INTEGER IS
		l_count	 NUMBER;
	BEGIN
		pkg_common_logging.set_client_info ('partition_exist');

		IF c_tab_partitions%ISOPEN THEN
			CLOSE c_tab_partitions;
		END IF;

		OPEN c_tab_partitions (my_tab_ident.owner,
									  my_tab_ident.table_name,
									  p_partition_name);

		FETCH c_tab_partitions
		INTO l_count;

		IF c_tab_partitions%ISOPEN THEN
			CLOSE c_tab_partitions;
		END IF;

		IF l_count > 0 THEN
			RETURN 1;
		END IF;

		RETURN 0;
	EXCEPTION
		WHEN OTHERS THEN
			IF c_tab_partitions%ISOPEN THEN
				CLOSE c_tab_partitions;
			END IF;

			RETURN 0;
			RAISE;
	END partition_exist;

	--
	--

	FUNCTION maxvalue_partition_exists (
		p_table_name	IN 	 user_tab_partitions.table_name%TYPE,
		p_part_name 		OUT user_tab_partitions.partition_name%TYPE
	)
		RETURN BOOLEAN IS
		v_ret 			BOOLEAN := FALSE;
		v_high_value	VARCHAR2 (1000);
	BEGIN
		SELECT	high_value, partition_name
		  INTO	v_high_value, p_part_name
		  FROM	all_tab_partitions
		 WHERE	table_name = my_tab_ident.table_name
					AND table_owner = my_tab_ident.owner
					AND partition_position =
							 (SELECT   MAX (partition_position)
								 FROM   all_tab_partitions
								WHERE   table_name = my_tab_ident.table_name
										  AND table_owner = my_tab_ident.owner);

		IF INSTR (v_high_value, 'MAXVALUE') > 0 THEN
			v_ret := TRUE;
		END IF;

		RETURN v_ret;
	EXCEPTION
		WHEN OTHERS THEN
			pkg_common_logging.write_log_error;
			RETURN v_ret;
	END maxvalue_partition_exists;


	PROCEDURE crpt_ (
		p_table_name					 user_tab_partitions.table_name%TYPE,
		p_partition_name				 user_tab_partitions.partition_name%TYPE,
		p_partitioning_key_value	 VARCHAR2,
		p_force							 BOOLEAN DEFAULT FALSE,
		p_tablespace_name 			 user_tab_partitions.tablespace_name%TYPE DEFAULT NULL
	) IS
		l_partition_bound_too_low	 EXCEPTION;
		PRAGMA EXCEPTION_INIT (l_partition_bound_too_low, -14074);

		l_partition_value_exist 	 EXCEPTION;
		PRAGMA EXCEPTION_INIT (l_partition_value_exist, -14080);

		p_max_part_name				 user_tab_partitions.partition_name%TYPE;
	BEGIN
		pkg_common_logging.set_client_info ('crpt_');



		IF partition_exist (p_table_name, p_partition_name) > 0 THEN
			pkg_common_logging.write_log (
					p_table_name
				|| ', partition '
				|| p_partition_name
				|| ' already exists',
				pkg_common_logging.set_info
			);
			RETURN;
		END IF;

		IF maxvalue_partition_exists (p_table_name, p_max_part_name) THEN
			pkg_common_logging.write_log (
				'found maxvalue partition -> split',
				pkg_common_logging.set_debug
			);

			IF check_global_index (p_table_name, p_force) THEN
				v_stmt :=
						'alter table '
					|| p_table_name
					|| ' split partition  '
					|| p_max_part_name
					|| ' at ('
					|| p_partitioning_key_value
					|| ') '
					|| ' into (partition '
					|| p_partition_name
					|| ', partition '
					|| p_max_part_name
					|| ') '
					|| ' update indexes';
			ELSE
				pkg_common_logging.write_log (
					'Global index exist and no force=true was set -> '
					|| 'not executing as operation may take a very long time'
				);
				RETURN;
			END IF;
		ELSE
			v_stmt :=
					'ALTER TABLE '
				|| p_table_name
				|| ' ADD PARTITION '
				|| p_partition_name
				|| ' VALUES LESS THAN ('
				|| p_partitioning_key_value
				|| ')'
				|| CASE
						WHEN p_tablespace_name IS NULL THEN ' '
						ELSE ' tablespace ' || p_tablespace_name
					END;
		END IF;

		pkg_common_logging.write_log (v_stmt, pkg_common_logging.set_debug);

		EXECUTE IMMEDIATE v_stmt;

		pkg_common_logging.write_log (
			'Partition ' || p_partition_name || ' added',
			pkg_common_logging.set_info
		);

        pkg_common_logging.write_log('Partition ' || p_partition_name || ' added');

	EXCEPTION
		WHEN l_partition_bound_too_low OR l_partition_value_exist THEN
			pkg_common_logging.write_log (
					p_table_name
				|| ', partition '
				|| p_partition_name
				|| ' partition_bound_too_low',
				pkg_common_logging.set_info
			);
		WHEN OTHERS THEN
			RAISE;
	END crpt_;

	--
	--

	PROCEDURE crpt (
		p_table_name			VARCHAR2,
		p_date_type 			VARCHAR2 := 'WEEK',
		p_day 					DATE := SYSDATE,
		p_force					BOOLEAN DEFAULT FALSE,
		p_tablespace_name 	user_tab_partitions.tablespace_name%TYPE DEFAULT NULL
	) IS
	BEGIN
		pkg_common_logging.init_log (
			'psm_pt.crpt',
			p_table_name,
				'crpt_ => '
			|| UPPER (p_date_type)
			|| ':'
			|| TO_CHAR (p_day, 'yyyymmdd')
			|| CASE
					WHEN p_tablespace_name IS NOT NULL THEN
						' tablespace ' || p_tablespace_name
				END
		);

		IF UPPER (p_date_type) NOT IN ('WEEK', 'MONTH', 'DAY', 'YEAR') THEN
			RAISE e_invalid_input;
		END IF;

		set_tab_ident (p_table_name);

		char_partitioned :=
			part_column_type (UPPER (p_table_name)) IN
				('CHAR', 'VARCHAR', 'VARCHAR2');



		crpt_ (
			UPPER (p_table_name),
			sh_partition_name (p_day, p_date_type),
			sh_partitioning_key_value (p_day, p_date_type),
			p_force,
			p_tablespace_name
		);

	--	pkg_common_logging.reset_log;
	EXCEPTION
		WHEN e_invalid_input THEN
			pkg_common_logging.write_log_error (
				p_table_name || '-> Partitioning Range invalid',
				-20040,
					'Partitioning Range was '
				|| p_date_type
				|| ' but must be WEEK,MONTH or DAY'
			);
			raise_application_error (-20040, 'Partitioning Range invalid');
		WHEN OTHERS THEN
			pkg_common_logging.write_log_error;
			RAISE;
	END crpt;


	PROCEDURE crpt IS
		CURSOR c_all_tabl IS
			SELECT	a.*
			  FROM	pt_config a, all_tables b
			 WHERE	a.owner = b.owner AND a.table_name = b.table_name;

		v_date_end		 DATE;
		v_date_run		 DATE := SYSDATE;

		l_force_split	 BOOLEAN DEFAULT FALSE;
	BEGIN
		pkg_common_logging.init_log (
			'psm_pt.crpt',
			'config from pt_config',
			'start'
		);


		FOR r_all_tabl IN c_all_tabl LOOP
			IF r_all_tabl.date_type NOT IN ('DAY', 'WEEK', 'MONTH') THEN
				RAISE e_invalid_input;
			END IF;

			v_date_end :=
				CASE
					WHEN r_all_tabl.date_type = 'DAY' THEN
						SYSDATE + r_all_tabl.intervall_adv
					WHEN r_all_tabl.date_type = 'MONTH' THEN
						ADD_MONTHS (SYSDATE, r_all_tabl.intervall_adv)
					WHEN r_all_tabl.date_type = 'WEEK' THEN
						SYSDATE + (7 * r_all_tabl.intervall_adv)
				END;

			v_date_run := SYSDATE;


			l_force_split := r_all_tabl.force_split IN ('Y');

			WHILE v_date_run <= v_date_end LOOP
				pkg_common_logging.set_action (
					r_all_tabl.owner || '.' || r_all_tabl.table_name
				);
				crpt (
					r_all_tabl.owner || '.' || r_all_tabl.table_name,
					r_all_tabl.date_type,
					v_date_run,
					l_force_split,
					r_all_tabl.tablespace_name
				);


				v_date_run :=
					CASE
						WHEN r_all_tabl.date_type = 'DAY' THEN
							v_date_run + 1
						WHEN r_all_tabl.date_type = 'MONTH' THEN
							ADD_MONTHS (v_date_run, 1)
						WHEN r_all_tabl.date_type = 'WEEK' THEN
							v_date_run + 7
					END;
			END LOOP;
		END LOOP;

		 pkg_common_logging.reset_log;
	EXCEPTION
		WHEN e_invalid_input THEN
			pkg_common_logging.write_log_error (
				' Partitioning Range invalid',
				-20040,
				'Partitioning Range must be WEEK,MONTH or DAY'
			);
			raise_application_error (-20040, 'Partitioning Range invalid');
		WHEN OTHERS THEN
			pkg_common_logging.write_log_error;
			RAISE;
	END crpt;


	PROCEDURE ident_partitions (
		p_table_name				  VARCHAR2,
		p_max_date					  DATE DEFAULT SYSDATE - 60,
		p_min_date					  DATE DEFAULT SYSDATE - 180,
		p_part_merge			 OUT t_part_merge,
		check_first_only	 IN	  BOOLEAN DEFAULT FALSE
	) IS
		v_high_value	VARCHAR2 (1000);
		v_part_date 	DATE;
		rec_id			NUMBER := 0;
	BEGIN
		char_partitioned :=
			part_column_type (p_table_name) IN ('CHAR', 'VARCHAR', 'VARCHAR2');
		pkg_common_logging.write_log (
			'check for mergeable partitions: ' || p_table_name,
			pkg_common_logging.set_info
		);
		pkg_common_logging.write_log (
				'p_min_date: '
			|| TO_CHAR (p_min_date, 'yyyymmdd')
			|| '; p_max_date:'
			|| TO_CHAR (p_max_date, 'yyyymmdd'),
			pkg_common_logging.set_info
		);


		FOR r_rec
			IN (SELECT		table_name, partition_name
					  FROM	all_tab_partitions
					 WHERE	table_name = my_tab_ident.table_name
								AND table_owner = my_tab_ident.owner
				 ORDER BY	partition_position) LOOP
			SELECT	high_value
			  INTO	v_high_value
			  FROM	all_tab_partitions
			 WHERE		 table_name = my_tab_ident.table_name
						AND table_owner = my_tab_ident.owner
						AND partition_name = r_rec.partition_name;

			IF INSTR (v_high_value, 'MAXVALUE') > 0 AND check_first_only THEN
				--
				--  in case we have a maxvalue we will abort
				--  check_first_only should not really be used when droptin partitions as we do not want to drop the maxvalue
				--
				pkg_common_logging.write_log (
					'found MAXVALUE partition -> end',
					pkg_common_logging.set_info
				);

				rec_id := rec_id + 1;
				p_part_merge (rec_id) := 'MAXVALUE';


				RETURN;
			END IF;


			IF char_partitioned THEN
				v_stmt :=
					'select to_date('
					|| CASE
							WHEN SUBSTR (v_high_value, 8, 2) IN
									  (p_lst_day_ident, '99') THEN
								SUBSTR (v_high_value, 1, 7) || '01'''
							ELSE
								v_high_value
						END
					|| ',''yyyymmdd'')  from dual';
			ELSE
				v_stmt := 'select ' || v_high_value || '  from dual';
			END IF;

			BEGIN
				EXECUTE IMMEDIATE v_stmt INTO v_part_date;

				pkg_common_logging.write_log (
						CASE WHEN char_partitioned THEN 'char' ELSE 'date' END
					|| ' partitioned, date:'
					|| TO_CHAR (v_part_date, 'yyyymmdd'),
					pkg_common_logging.set_debug
				);
				b_check_partition_success := TRUE;
			EXCEPTION
				WHEN OTHERS THEN
					v_part_date := NULL;
			END;

			IF v_part_date BETWEEN p_min_date + 1 AND p_max_date + 1 THEN
				rec_id := rec_id + 1;
				p_part_merge (rec_id) := r_rec.partition_name;
				pkg_common_logging.write_log (
					'partition ' || r_rec.partition_name || ' found in date range',
					pkg_common_logging.set_info
				);

				IF check_first_only THEN
					RETURN;
				END IF;
			ELSE
				pkg_common_logging.write_log (
					'partition ' || r_rec.partition_name || ' not in date range',
					pkg_common_logging.set_debug
				);
			END IF;
		END LOOP;
	EXCEPTION
		WHEN OTHERS THEN
			pkg_common_logging.write_log_error;
			RAISE;
	END;



	PROCEDURE merge_all_partitions (
		p_table_name		VARCHAR2,
		p_min_date			DATE DEFAULT SYSDATE - 180,
		p_max_date			DATE DEFAULT SYSDATE - 60,
		force_merge 	IN BOOLEAN DEFAULT FALSE
	) IS
	BEGIN
		pkg_common_logging.init_log (
			'psm_pt.merge_partition',
			p_table_name,
				'daterange: '
			|| TO_CHAR (p_min_date, 'yyyymmdd')
			|| ' to '
			|| TO_CHAR (p_max_date, 'yyyymmdd')
		);



		IF check_global_index (p_table_name, force_merge) THEN
			pkg_common_logging.write_log (
				p_table_name
				|| ' has not global indexes or force_merge was set -> OK to run',
				pkg_common_logging.set_debug
			);
			ident_partitions (
				p_table_name,
				p_max_date,
				p_min_date,
				my_part_merge
			);


			IF my_part_merge.COUNT > 1 THEN
				FOR i IN my_part_merge.FIRST .. (my_part_merge.LAST - 1) LOOP
					pkg_common_logging.set_action (my_part_merge (i));

					v_stmt :=
							'alter table '
						|| p_table_name
						|| ' merge partitions '
						|| my_part_merge (i)
						|| ', '
						|| my_part_merge (i + 1)
						|| ' into partition '
						|| my_part_merge (i + 1)
						|| ' UPDATE INDEXES';

					pkg_common_logging.write_log (
						v_stmt,
						pkg_common_logging.set_debug
					);

					EXECUTE IMMEDIATE v_stmt;

					pkg_common_logging.write_log (
						'merged into ' || my_part_merge (i + 1),
						pkg_common_logging.set_info
					);
				END LOOP;

				pkg_common_logging.set_action (p_table_name);

				IF p_max_date = LAST_DAY (p_max_date) THEN
					v_stmt :=
							'alter table '
						|| p_table_name
						|| ' rename partition '
						|| my_part_merge (my_part_merge.LAST)
						|| ' to '
						|| sh_partition_name (p_max_date, 'MONTH');

					pkg_common_logging.write_log (
						v_stmt,
						pkg_common_logging.set_debug
					);

					EXECUTE IMMEDIATE v_stmt;

					pkg_common_logging.write_log (
							'renamed '
						|| my_part_merge (my_part_merge.LAST)
						|| ' to '
						|| sh_partition_name (p_max_date, 'MONTH'),
						pkg_common_logging.set_info
					);
				ELSIF my_part_merge.COUNT = 1 THEN
					pkg_common_logging.write_log (
						my_part_merge (my_part_merge.LAST)
						|| ' is not the last partition for this month-> no rename',
						pkg_common_logging.set_info
					);
				ELSE
					pkg_common_logging.write_log (
						'no partitions found',
						pkg_common_logging.set_debug
					);
				END IF;
			ELSE
				pkg_common_logging.write_log (
						'found only one partition: '
					|| my_part_merge (my_part_merge.LAST)
					|| ' -> can not merge',
					pkg_common_logging.set_info
				);
			END IF;
		ELSE
			pkg_common_logging.write_log (
				p_table_name || ' has global indexes and force_merge was not set',
				pkg_common_logging.set_info
			);
		END IF;



		pkg_common_logging.reset_log;
	EXCEPTION
		WHEN OTHERS THEN
			pkg_common_logging.write_log_error;
			RAISE;
	END;



	PROCEDURE merge_partitions (
		p_table_name		VARCHAR2,
		p_min_date			DATE DEFAULT SYSDATE - 180,
		p_max_date			DATE DEFAULT SYSDATE - 60,
		force_merge 	IN BOOLEAN DEFAULT FALSE
	) IS
		v_cur_date	 DATE := p_min_date;
	BEGIN
		pkg_common_logging.init_log (
			'psm_pt.merge_partition',
			p_table_name,
				'daterange: '
			|| TO_CHAR (p_min_date, 'yyyymmdd')
			|| ' to '
			|| TO_CHAR (p_max_date, 'yyyymmdd')
		);

		set_tab_ident (p_table_name);

		LOOP
			pkg_common_logging.write_log (
				'call merge for ' || TO_CHAR (v_cur_date, 'yyyymmdd') || ' to '
				|| TO_CHAR (
						LEAST (LAST_DAY (v_cur_date), p_max_date),
						'yyyymmdd'
					),
				pkg_common_logging.set_debug
			);

			merge_all_partitions (
				p_table_name,
				v_cur_date,
				LEAST (LAST_DAY (v_cur_date), p_max_date),
				force_merge
			);

			v_cur_date := LEAST (LAST_DAY (v_cur_date), p_max_date) + 1;

			EXIT WHEN v_cur_date >= p_max_date;
		END LOOP;

		pkg_common_logging.reset_log;
	EXCEPTION
		WHEN OTHERS THEN
			pkg_common_logging.write_log_error;
			RAISE;
	END;

	PROCEDURE clean_partitions (
		p_table_name		VARCHAR2,
		p_part				VARCHAR2,
		force_drop		IN BOOLEAN DEFAULT FALSE
	) IS
	BEGIN
		pkg_common_logging.init_log (
			'psm_pt.clean_partitions',
			p_table_name,
			'clean_partition: ' || p_part
		);
		set_tab_ident (p_table_name);

		IF check_global_index (p_table_name, force_drop) THEN
			v_stmt :=
					'alter table '
				|| p_table_name
				|| ' truncate   partition '
				|| p_part;
			pkg_common_logging.write_log (v_stmt, pkg_common_logging.set_debug);

			EXECUTE IMMEDIATE v_stmt;
		ELSE
			pkg_common_logging.write_log (
				p_table_name || ' has global indexes and truncate was not done',
				pkg_common_logging.set_info
			);
		END IF;

		pkg_common_logging.reset_log;
	EXCEPTION
		WHEN OTHERS THEN
			pkg_common_logging.write_log_error;
			RAISE;
	END clean_partitions;


	PROCEDURE clean_partitions (
		p_table_name		  VARCHAR2,
		p_max_date			  DATE DEFAULT SYSDATE - 180,
		force_drop		  IN BOOLEAN DEFAULT FALSE,
		drop_physicaly   IN BOOLEAN DEFAULT FALSE
	) IS
		v_high_value	VARCHAR2 (1000);
		v_id				NUMBER;
		v_part_date 	DATE;
	BEGIN
		pkg_common_logging.init_log (
			'psm_pt.clean_partitions',
			p_table_name,
			'clean_partitions'
		);
		set_tab_ident (p_table_name);

		char_partitioned :=
			part_column_type (p_table_name) IN ('CHAR', 'VARCHAR', 'VARCHAR2');

		IF check_global_index (p_table_name, force_drop) THEN
			ident_partitions (
				p_table_name,
				p_max_date,
				c_first_day,
				my_part_merge
			);


			-- identify partitions
			FOR i IN my_part_merge.FIRST .. my_part_merge.LAST LOOP
				v_stmt :=
						'alter table '
					|| p_table_name
					|| CASE WHEN drop_physicaly THEN ' drop' ELSE ' truncate' END
					|| '  partition '
					|| my_part_merge (i);

				pkg_common_logging.write_log (
					v_stmt,
					pkg_common_logging.set_debug
				);

				EXECUTE IMMEDIATE v_stmt;

				pkg_common_logging.write_log (
					my_part_merge (i)
					|| CASE WHEN drop_physicaly THEN ' drop' ELSE ' truncate' END,
					pkg_common_logging.set_info
				);
			END LOOP;
		ELSE
			pkg_common_logging.write_log (
				p_table_name || ' has global indexes and force_merge was not set',
				pkg_common_logging.set_info
			);
		END IF;

		pkg_common_logging.reset_log;
	EXCEPTION
		WHEN OTHERS THEN
			pkg_common_logging.write_log_error;
			RAISE;
	END clean_partitions;


	PROCEDURE truncate_sub_part (
		p_table_name		VARCHAR2,
		p_sub_part			VARCHAR2,
		force_drop		IN BOOLEAN DEFAULT FALSE
	) IS
		v_id	 NUMBER;
	BEGIN
		set_tab_ident (p_table_name);

		IF check_global_index (p_table_name, force_drop) THEN
			EXECUTE IMMEDIATE
					'alter table '
				|| p_table_name
				|| ' truncate subpartition '
				|| p_sub_part;
		END IF;
	EXCEPTION
		WHEN OTHERS THEN
			RAISE;
	END truncate_sub_part;



	PROCEDURE drop_part (
		p_table_name		VARCHAR2,
		p_part				VARCHAR2,
		force_drop		IN BOOLEAN DEFAULT FALSE
	) IS
	BEGIN
		set_tab_ident (p_table_name);

		IF check_global_index (p_table_name, force_drop) THEN
			EXECUTE IMMEDIATE
				'alter table ' || p_table_name || ' drop  partition  ' || p_part;
		END IF;
	END drop_part;

	FUNCTION show_version
		RETURN VARCHAR2 IS
	BEGIN
		RETURN 'psm_pt version is ' || c_version;
	END show_version;

	--


	FUNCTION build_return_stmt (
		my_part_merge	 IN t_part_merge,
		p_table_name	 IN VARCHAR2
	)
		RETURN VARCHAR2 IS
		v_return   VARCHAR2 (200);
	BEGIN
		IF my_part_merge.COUNT > 0
			AND INSTR (my_part_merge (my_part_merge.FIRST), 'MAXVALUE') = 0 THEN
			v_return :=
					'OK: partition '
				|| my_part_merge (my_part_merge.LAST)
				|| ' for '
				|| p_table_name;
		ELSIF my_part_merge.COUNT > 0
				AND INSTR (my_part_merge (my_part_merge.FIRST), 'MAXVALUE') > 0 THEN
			v_return := 'Warn: Maxvalue for ' || p_table_name;
		ELSIF b_check_partition_success THEN
			v_return := 'ERROR: Partition missing for ' || p_table_name;

			write_alert_log (-20001, v_stmt || ' for table ' || p_table_name);
		END IF;

		RETURN v_return;
	END build_return_stmt;

	FUNCTION f_check_partitions (
		p_table_name			 IN VARCHAR2,
		p_min_required_date	 IN DATE DEFAULT SYSDATE + 7
	)
		RETURN xdb.xdb$string_list_t
		PIPELINED IS
	BEGIN
		set_tab_ident (p_table_name);
		my_part_merge.delete;
		ident_partitions (
			p_table_name,
			p_min_required_date + 1000,
			p_min_required_date,
			my_part_merge,
			TRUE
		);


		PIPE ROW (build_return_stmt (my_part_merge, p_table_name));
	END f_check_partitions;


	FUNCTION f_check_partitions (
		p_min_required_date IN DATE DEFAULT SYSDATE + 7
	)
		RETURN xdb.xdb$string_list_t
		PIPELINED IS
	BEGIN
		pkg_common_logging.init_log (
			'psm_pt.f_check_partitions',
			USER,
			TO_CHAR (p_min_required_date, 'yyyymmdd')
		);

		FOR r_tables
			IN (SELECT	 DISTINCT a.owner || '.' || a.name tablename
					FROM	 all_part_key_columns a, all_tab_columns b
				  WHERE		  object_type = 'TABLE'
							 AND a.name = b.table_name
							 AND a.column_name = b.column_name
							 AND a.owner = b.owner
							 AND b.data_type IN
									  ('DATE',
										'TIMESTAMP(6)',
										'TIMESTAMP(6) WITH TIME ZONE')) LOOP
			pkg_common_logging.write_log (
				r_tables.tablename,
				pkg_common_logging.set_info
			);
			my_part_merge.delete;
			set_tab_ident (r_tables.tablename);
			b_check_partition_success := FALSE;

			ident_partitions (
				r_tables.tablename,
				p_min_required_date + 200,
				p_min_required_date,
				my_part_merge,
				TRUE
			);



			PIPE ROW (build_return_stmt (my_part_merge, r_tables.tablename));
		END LOOP;

		pkg_common_logging.reset_log;
	EXCEPTION
		WHEN OTHERS THEN
			pkg_common_logging.write_log_error;
			RAISE;
	END f_check_partitions;
END psm_pt;
/

CREATE OR REPLACE package pkg_tab_part
authid current_user

as

 gv_ddl_source_table varchar2(64);

 procedure set_ddl_source_table (v_name in varchar2);
 
 procedure create_base_tab(v_col_name in user_tab_columns.column_name%type, 
                v_setoff_value in date default sysdate -10);
    

procedure add_parts(v_period in varchar2,v_min_dt in date default sysdate - 800, v_max_dt in date default sysdate +30);

procedure run_redefinition;

end pkg_tab_part;

/


CREATE OR REPLACE package body pkg_tab_part



as

v_SESSION_MODULE varchar2(64) := 'pkg_tab_part.run_script'; 

r_tab_def all_tables%rowtype;

v_stmt varchar2(32000);

type t_tab_ident is record (owner all_tables.owner%type, table_name all_tables.table_name%type);
    
my_tab_ident t_tab_ident ;
    

procedure 

get_tab_def 
is 


begin

   my_tab_ident.owner := upper(nvl(substr(pkg_tab_part.gv_ddl_source_table,1,instr(pkg_tab_part.gv_ddl_source_table,'.') - 1), user) ) ;
    my_tab_ident.table_name := upper(substr(pkg_tab_part.gv_ddl_source_table,instr(pkg_tab_part.gv_ddl_source_table,'.')+1 ,100));
     pkg_common_logging.write_log('table owner: '||  my_tab_ident.owner  ,pkg_common_logging.set_debug);
     pkg_common_logging.write_log('table_name: '||  my_tab_ident.table_name  ,pkg_common_logging.set_debug);

   DBMS_METADATA.SET_TRANSFORM_PARAM(DBMS_METADATA.SESSION_TRANSFORM,'SEGMENT_ATTRIBUTES',FALSE);
 
   DBMS_METADATA.SET_TRANSFORM_PARAM(DBMS_METADATA.SESSION_TRANSFORM,'CONSTRAINTS',FALSE);
 
   dbms_metadata.set_transform_param(   dbms_metadata.session_transform, 'PRETTY', false);

execute immediate 'select * from all_tables where table_name = :b1 and owner = :b2' into r_tab_def using my_tab_ident.table_name, my_tab_ident.owner ;

end get_tab_def;


procedure  add_partition_stmt(v_col_name in user_tab_columns.column_name%type, 
                v_setoff_value in date, v_part_stmt out varchar2)
                
 is
 
 begin
 
 v_part_stmt := 'partition by range ('||v_col_name||')  (partition P_INIT values less than (to_date (''' ||
                TO_CHAR (v_setoff_value + 1,'yyyy_mm_dd')||'''' ||',''yyyy_mm_dd'')))';
 
 
  
  
 end add_partition_stmt;     


 procedure set_ddl_source_table (v_name in varchar2) is
 
 begin

    pkg_tab_part.gv_ddl_source_table := v_name;
 
 
 end;
 
 procedure run_script (v_sql in varchar2, v_log_info in varchar2 default null, fail_on_error in boolean default true)
is

begin

    pkg_common_logging.write_log(substr(v_sql,1,512), pkg_common_logging.set_info);
execute immediate v_sql;
    pkg_common_logging.r_comlog_app_log.NO_OF_RECORDS := sql%rowcount;

    if v_log_info is null then  
        pkg_common_logging.write_log_sel;
    else
        pkg_common_logging.write_log(v_log_info);
    end if;



exception
    when others  then 
      pkg_common_logging.write_log_error;
      if  fail_on_error then
       raise;    
      end if;
      
      
end run_script;


 procedure create_base_tab(v_col_name in user_tab_columns.column_name%type, 
                v_setoff_value in date default sysdate -10)
                
               as
               
v_part_stmt varchar2(500);
          
begin               
   pkg_common_logging.init_log(v_SESSION_MODULE,'create_base_tab: '||pkg_tab_part.gv_ddl_source_table,'col: '||v_col_name );  
        get_tab_def;
  pkg_common_logging.write_log('tab details '||r_tab_def.owner||'.'||r_tab_def.table_name,pkg_common_logging.set_info);
 
    
   v_stmt := substr(dbms_metadata.get_ddl ('TABLE',r_tab_def.table_name,r_tab_def.owner),1,32000);
   v_stmt := replace (v_stmt,r_tab_def.table_name,substr(r_tab_def.table_name,1,26)||'_PRT');
   
    pkg_common_logging.write_log('ddl created',pkg_common_logging.set_info);
    v_stmt := v_stmt ||' tablespace '||r_tab_def.tablespace_name;
    pkg_common_logging.write_log('tablespace '||r_tab_def.tablespace_name,pkg_common_logging.set_info);
     
    add_partition_stmt(v_col_name,v_setoff_value, v_part_stmt);
    
     v_stmt := v_stmt ||' '||v_part_stmt;
     
    pkg_common_logging.write_log(v_part_stmt, pkg_common_logging.set_debug);
    
   
   run_script(v_stmt);
    pkg_common_logging.write_log('table '||substr(r_tab_def.table_name,1,26)||'_PRT created');
    
   

exception
    when others  then 
      pkg_common_logging.write_log_error;
     raise;    
     
     
end     create_base_tab;  



procedure add_parts(v_period in varchar2,v_min_dt in date default sysdate - 800, v_max_dt in date default sysdate +30)
is

v_inc  number := 1;
e_invalid_period exception;
begin
 pkg_common_logging.init_log(v_SESSION_MODULE,'add_parts: '||v_period,'max date: '||to_char(v_max_dt,'yyyymmdd')  );  
 
 get_tab_def;
 

 
 case v_period 
    when 'WEEK' then
        v_inc :=  trunc((v_max_dt  -  v_min_dt ) / 7) +1 ;
    when 'DAY' then 
         v_inc :=  (v_max_dt  -  v_min_dt );
    when 'MONTH' then
          v_inc := months_between(v_max_dt , v_min_dt ) +1;   
     else
        raise e_invalid_period;
     end case;
     
  for i in 1 ..  v_inc loop
  
  
  case v_period 
    when 'WEEK' then
        
        psm_pt.crpt(p_table_name => r_tab_def.owner ||'.'||substr(r_tab_def.table_name,1,26)||'_PRT',p_date_type => v_period , p_day => v_min_dt + I*7);
        
    when 'DAY' then 
          psm_pt.crpt(p_table_name => r_tab_def.owner ||'.'||substr(r_tab_def.table_name,1,26)||'_PRT',p_date_type => v_period , p_day => v_min_dt + I);
    when 'MONTH' then
         psm_pt.crpt(p_table_name =>  r_tab_def.owner ||'.'||substr(r_tab_def.table_name,1,26)||'_PRT',p_date_type => v_period , p_day => add_months(v_min_dt , I) ); 
     end case;
     
     
  
  
  end loop;  
       

exception
  when e_invalid_period then
        pkg_common_logging.write_log_error(' Partitioning Range invalid',-20040,'Partitioning Range was '||v_period||' but must be WEEK,MONTH or DAY');
        raise_application_error(-20040,'Partitioning Range invalid');
    when others  then 
      pkg_common_logging.write_log_error;
     raise;   
end add_parts;


procedure run_redefinition
is

    my_constraints all_constraints%rowtype;
    my_part_cols all_PART_KEY_COLUMNS%rowtype;
    my_index all_indexes%rowtype;
    v_err number;
begin
 pkg_common_logging.init_log(v_SESSION_MODULE, pkg_tab_part.gv_ddl_source_table,'run_redefinition');  
 get_tab_def;
 
 
  dbms_redefinition.can_redef_table(uname   => r_tab_def.owner,
                                        tname => r_tab_def.table_name);
   
   pkg_common_logging.write_log('Redefinition is possbile, starting now');
   
  

  DBMS_REDEFINITION.START_REDEF_TABLE(uname => r_tab_def.owner, 
                                           orig_table => r_tab_def.table_name, 
                                           int_table => substr(r_tab_def.table_name,1,26)||'_PRT');
                
  pkg_common_logging.write_log('Redefinition done');
  
  select *  into my_constraints from all_constraints s where owner =  r_tab_def.owner  
    and table_name =  r_tab_def.table_name and constraint_type = 'P';
    
  select *  into my_part_cols from   all_PART_KEY_COLUMNS  where  owner =  r_tab_def.owner 
    and name = substr(r_tab_def.table_name,1,26)||'_PRT';
    
  select * into my_index from all_indexes where  owner =  r_tab_def.owner  
    and index_name = my_constraints.index_name;
    
    
  v_stmt := substr( dbms_metadata.get_ddl ('INDEX',my_constraints.index_name, r_tab_def.owner),1,32000);
  
  v_stmt := replace(v_stmt, my_constraints.index_name,substr(my_constraints.index_name,1,26)||'_PRT');
  v_stmt := replace(v_stmt, '"'||r_tab_def.table_name||'"',  '"'||substr(r_tab_def.table_name,1,26)||'_PRT'||'"');
  
  v_stmt := replace(v_stmt, ')' ,', '||my_part_cols.column_name||
                    ') local parallel 3 tablespace '||my_index.tablespace_name);
                    
  run_script(  v_stmt);  
  pkg_common_logging.write_log('Index created');           
 
 v_stmt :=  substr( dbms_metadata.get_ddl ('CONSTRAINT',my_constraints.constraint_name, r_tab_def.owner),1,32000);
 
  v_stmt :=  replace(v_stmt,my_constraints.constraint_name,substr(my_constraints.constraint_name,1,26)||'_PRT');
   v_stmt :=  replace(v_stmt,'ENABLE','NOVALIDATE');
   v_stmt := replace(v_stmt, ')' ,', '||my_part_cols.column_name||  ') ');
     v_stmt := replace(v_stmt, '"'||r_tab_def.table_name||'"',  '"'||substr(r_tab_def.table_name,1,26)||'_PRT'||'"');


 run_script(  v_stmt);  
 
 pkg_common_logging.write_log('PK created'); 
 
 
  DBMS_REDEFINITION.register_dependent_object(r_tab_def.owner, 
                        r_tab_def.table_name, 
                        substr(r_tab_def.table_name,1,26)||'_PRT',
                         DBMS_REDEFINITION.cons_index, 
                       r_tab_def.owner,
                         my_constraints.index_name,
                         substr(my_constraints.index_name,1,26)||'_PRT');
 
 pkg_common_logging.write_log('Index Registered'); 

 DBMS_REDEFINITION.register_dependent_object(  r_tab_def.owner, 
                          r_tab_def.table_name, 
                         substr(r_tab_def.table_name,1,26)||'_PRT',
                         DBMS_REDEFINITION.cons_constraint, 
                        r_tab_def.owner,
                        my_constraints.constraint_name,
                         substr(my_constraints.constraint_name,1,26)||'_PRT');

  pkg_common_logging.write_log('Constraint Registered'); 
  
 dbms_redefinition.copy_table_dependents(r_tab_def.owner, 
                         r_tab_def.table_name, 
                          substr(r_tab_def.table_name,1,26)||'_PRT',
                         1, 
                         TRUE, 
                         TRUE, 
                         TRUE , 
                         true, 
                         v_err);
   pkg_common_logging.write_log('dbms_redefinition; number rof errors: '||to_char(v_err)); 
 

 DBMS_REDEFINITION.FINISH_REDEF_TABLE(r_tab_def.owner, 
                       r_tab_def.table_name, 
                       substr(r_tab_def.table_name,1,26)||'_PRT');

  pkg_common_logging.write_log('Redef successfully done!');
  
  pkg_common_logging.reset_log;
 
exception 
when others  then 
      pkg_common_logging.write_log_error;
     raise;   
end run_redefinition;

end pkg_tab_part;
/
