DBA Data[Home] [Help]

PACKAGE BODY: MDSYS.SDO_ROUTER_PARTITION

Source


1 PACKAGE BODY SDO_ROUTER_PARTITION AS
2 
3 -- Partitioning log file
4   part_log_file   utl_file.file_type := NULL;
5   schema_name     VARCHAR2(64) := NULL;
6   JAVA_ERROR      EXCEPTION;
7   PARAMETER_ERROR EXCEPTION;
8 
9 --
10 -- Logging
11 --
12 
13 --- Log a message
14 PROCEDURE log_message(message IN VARCHAR2, show_time IN BOOLEAN DEFAULT TRUE)
15 IS
16 BEGIN
17 
18   if  ( utl_file.is_open(part_log_file) = FALSE )  then
19      return;
20   end if;
21   IF ( show_time ) THEN
22     utl_file.put_line (part_log_file, sdo_util.number_to_char(sysdate,'Dy fmMon DD HH24:MI:SS YYYY'));
23   END IF;
24 
25   utl_file.put_line (part_log_file, message);
26   utl_file.fflush(part_log_file);
27 
28 EXCEPTION
29   WHEN OTHERS THEN
30     raise_application_error(-20002, 'PLSQL Error Writing Log File');
31 END log_message;
32 
33 --- Open the log file
34 FUNCTION open_log_file(log_file_name IN VARCHAR2)
35 RETURN VARCHAR2 AS
36   full_file_name  VARCHAR2(256);
37   stmt         VARCHAR2(256);
38 BEGIN
39   BEGIN
40     -- Open the routers partition log file
41 
42     IF (NOT utl_file.is_open(part_log_file)) THEN
43       part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
44     END IF;
45 
46     stmt := 'SELECT directory_path FROM all_directories
47              WHERE directory_name=''SDO_ROUTER_LOG_DIR''';
48     EXECUTE IMMEDIATE stmt into full_file_name;
49 
50     -- Make sure the path is terminated with a /
51     if (instr(full_file_name, '/', length(full_file_name)) = 0)
52     then full_file_name := full_file_name || '/';
53     end if;
54 
55     full_file_name := full_file_name || log_file_name;
56 
57   EXCEPTION
58     WHEN utl_file.invalid_path THEN
59       raise_application_error(-20006, '[ERROR] SDO_ROUTER_LOG_DIR directory not found');
60     WHEN utl_file.invalid_operation THEN
61       raise_application_error(-20006, '[ERROR] bad directory path specified for SDO_ROUTER_LOG_DIR directory');
62     WHEN OTHERS THEN
63       raise_application_error(-20001, '[ERROR] could not open log file');
64   END;
65 
66   RETURN full_file_name;
67 END open_log_file;
68 
69 --- Create the SDO Router log directory
70 PROCEDURE create_sdo_router_log_dir(
71              router_schema    IN VARCHAR2,
72              new_dir_path     IN VARCHAR2 DEFAULT NULL)
73 IS
74   current_schema VARCHAR2(32) := sys_context('userenv', 'CURRENT_SCHEMA');
75   directory_path VARCHAR2(256) := NULL;
76   row_count      NUMBER := 0;
77   stmt           VARCHAR2(256);
78 BEGIN
79   -- Make sure current user isn't one of the two being granted privs
80   if (current_schema = UPPER(router_schema) OR current_schema = 'MDSYS')
81   then raise_application_error(-20010, '[ERROR] you may not GRANT privileges to yourself');
82   end if;
83 
84   -- Make sure 2 schemas get access to SDO_ROUTER_LOG_DIR
85   if (UPPER(router_schema) = 'MDSYS')
86   then raise_application_error(-20014, '[ERROR] MDSYS schema specified, please use a Routeserver schema');
87   end if;
88 
89   -- Make sure the Routeserver schema specified actually exists
90   SELECT COUNT(*) INTO row_count
91   FROM all_users
92   WHERE username = UPPER(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(router_schema));
93 
94   if (row_count = 0)
95   then raise_application_error(-20011, '[ERROR] schema does not exist');
96   end if;
97 
98   if (new_dir_path is NULL)
99   then
100     SELECT COUNT(*) INTO row_count
101     FROM all_directories
102     WHERE directory_name = 'SDO_ROUTER_LOG_DIR';
103 
104     -- If SDO_ROUTER_LOG_DIR does not already exist the new_dir_path parameter
105     -- must be specified. Raise an error if the directory does not exist and
106     -- the parameter was not specified.
107     if (row_count = 0)
108     then raise_application_error(-20012, '[ERROR] SDO_ROUTER_LOG_DIR directory does not exist');
109     end if;
110 
111     SELECT directory_path INTO directory_path
112     FROM all_directories
113     WHERE directory_name = 'SDO_ROUTER_LOG_DIR';
114 
115     directory_path := replace(directory_path, '\', '/');
116   else
117     directory_path := replace(new_dir_path, '\', '/');
118 
119     -- Be sure the path is not relative. The first character or
120     -- the first character following the : (Windows) must be /
121     -- if / is not the first character, the : must exist and
122     -- not be the very first character
123     if (SUBSTR(directory_path, 1, 1) != '/') AND
124        (INSTR(directory_path, ':') < 2 OR
125         SUBSTR(directory_path, INSTR(directory_path, ':')+1, 1) != '/')
126     then raise_application_error(-20013, '[ERROR] malformed directory path specified');
127     end if;
128   end if;
129 
130   -- Make sure the path is terminated with a /
131   if (instr(directory_path, '/', length(directory_path)) = 0)
132   then directory_path := directory_path || '/';
133   end if;
134 
135   -- Create the directory
136   stmt := 'CREATE OR REPLACE DIRECTORY sdo_router_log_dir as '||
137                 SYS.DBMS_ASSERT.ENQUOTE_LITERAL(directory_path);
138   EXECUTE IMMEDIATE stmt;
139 
140   directory_path := directory_path || '*';
141 
142   -- Grant privileges to the directory to the Router user and MDSYS
143   stmt := 'GRANT read, write ON DIRECTORY sdo_router_log_dir TO ' || UPPER(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(router_schema));
144   EXECUTE IMMEDIATE stmt;
145   dbms_java.grant_permission(
146     UPPER(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(router_schema)), 'java.io.FilePermission',
147     directory_path, 'read,write');
148 
149   EXECUTE IMMEDIATE 'GRANT read, write ON DIRECTORY sdo_router_log_dir TO MDSYS';
150   dbms_java.grant_permission(
151     'MDSYS', 'java.io.FilePermission',
152     directory_path, 'read,write');
153 
154 END create_sdo_router_log_dir;
155 
156 PROCEDURE validate_sdo_router_log_dir(log_file_name IN VARCHAR2 := 'sdo_router_partition.log')
157 IS
158   full_file_name VARCHAR2(256);
159   schema_name    VARCHAR2(32):= sys_context('userenv', 'CURRENT_SCHEMA');
160 BEGIN
161   -- From PLSQL
162   full_file_name := open_log_file(log_file_name);
163   log_message('[INFO] PLSQL logging OK (' || schema_name || ')');
164   utl_file.fclose(part_log_file);
165 
166   -- From Java
167   elocation_validate_logfile(full_file_name, schema_name);
168 
169 END validate_sdo_router_log_dir;
170 
171 --
172 -- check if the given constraint exists
173 --
174 FUNCTION constraint_exists(constraint_name IN VARCHAR2)
175   RETURN VARCHAR2
176 IS
177   stmt  VARCHAR2(256);
178   no    NUMBER := 0;
179 BEGIN
180   stmt := 'SELECT COUNT(*) FROM user_constraints WHERE CONSTRAINT_NAME = :name';
181   EXECUTE IMMEDIATE stmt into no using nls_upper(constraint_name);
182   IF (no = 1) THEN
183     RETURN 'TRUE';
184   ELSE
185     RETURN 'FALSE';
186   END IF;
187 END constraint_exists;
188 
189 --
190 -- check if the given index exists
191 --
192 FUNCTION index_exists(index_name IN VARCHAR2)
193   RETURN VARCHAR2
194 IS
195   stmt  VARCHAR2(256);
196   no    NUMBER := 0;
197 BEGIN
198   stmt := 'SELECT COUNT(*) FROM IND WHERE INDEX_NAME = :name';
199   EXECUTE IMMEDIATE stmt into no using nls_upper(index_name);
200   IF (no = 1) THEN
201     RETURN 'TRUE';
202   ELSE
203     RETURN 'FALSE';
204   END IF;
205 END index_exists;
206 
207 --
208 -- check if the given network exists
209 --
210 FUNCTION network_exists(network_name IN VARCHAR2)
211   RETURN VARCHAR2
212 IS
213   md_stmt  VARCHAR2(256);
214   ud_stmt  VARCHAR2(256);
215   no    NUMBER := 0;
216 BEGIN
217   md_stmt := 'SELECT COUNT(*) FROM USER_SDO_NETWORK_METADATA WHERE NETWORK = :name';
218   EXECUTE IMMEDIATE md_stmt into no using nls_upper(network_name);
219   IF (no = 1) THEN
220     ud_stmt := 'SELECT COUNT(*) FROM USER_SDO_NETWORK_USER_DATA WHERE NETWORK = :name';
221     EXECUTE IMMEDIATE ud_stmt into no using nls_upper(network_name);
222         IF (no > 0) THEN
223       RETURN 'TRUE';
224     END IF;
225   END IF;
226 
227   RETURN 'FALSE';
228 END network_exists;
229 
230 --
231 -- check if the given table exists
232 --
233 FUNCTION table_exists(tab_name IN VARCHAR2)
234   RETURN VARCHAR2
235 IS
236   stmt  VARCHAR2(256);
237   no    NUMBER := 0;
238 BEGIN
239   stmt := 'SELECT COUNT(*) FROM TAB WHERE TNAME = :name';
240   EXECUTE IMMEDIATE stmt into no using nls_upper(tab_name);
241   IF (no = 1) THEN
242     RETURN 'TRUE';
243   ELSE
244     RETURN 'FALSE';
245   END IF;
246 END table_exists;
247 
248 --
249 -- disk based graph partition functions/procedures
250 -- based on moment of inertia appraoch
251 --
252 FUNCTION min_eigenvector(sum_x2 IN NUMBER, sum_y2 IN NUMBER, sum_xy IN NUMBER)
253   RETURN mdsys.vector_2d
254 IS
255   lamda     NUMBER := 0;
256   tmp_sum   NUMBER := 0;
257   k         NUMBER := 0;
258   eigenvector_1 NUMBER := 0;
259   eigenvector_2 NUMBER := 0;
260 BEGIN
261   tmp_sum := sum_x2 + sum_y2;
262   lamda := (tmp_sum -
263     sqrt(tmp_sum*tmp_sum -4.0*(sum_x2*sum_y2-sum_xy*sum_xy)))/2;
264 
265   IF (sum_xy = 0 or (sum_x2-lamda) = 0 ) THEN
266     IF (sum_x2 > sum_y2) THEN
267       eigenvector_1 := 0;
268       eigenvector_2 := 1.0;
269     ELSE
270       eigenvector_1 := 1.0;
271       eigenvector_2 := 0;
272     END IF;
273   ELSE
274     k := -sum_xy/(sum_x2-lamda);
275     eigenvector_2 := 1.0/sqrt(k*k+1.0);
276     eigenvector_1 := k*eigenvector_2;
277   END IF;
278 
279   RETURN mdsys.vector_2d(eigenvector_1,eigenvector_2);
280 
281 END min_eigenvector;
282 
283 --
284 -- Return a negative value if the value falls in the given range (start_m,end_m)
285 --
286 FUNCTION adjust_m(start_m IN NUMBER, end_m IN NUMBER, m IN NUMBER)
287   RETURN NUMBER
288 IS
289 BEGIN
290   IF (m >= start_m AND m <= end_m) THEN
291     RETURN -m;
292   ELSE
293     RETURN m;
294   END IF;
295 END adjust_m;
296 
297 --
298 -- return the p_id based on the given m value (m > 0) ? pid : (pid+1)
299 --
300 FUNCTION get_pid(m IN NUMBER, pid IN NUMBER)
301   RETURN NUMBER
302 IS
303 BEGIN
304   IF ( m < 0 ) THEN
305     RETURN pid;
306   ELSE
307     RETURN pid+1;
308   END IF;
309 
310 END get_pid;
311 
312 --
313 --
314 --
315 PROCEDURE adjust_final_pid(p_tab_name IN VARCHAR2)
316 IS
317   min_pid NUMBER;
318   stmt VARCHAR2(256);
319 BEGIN
320 
321   EXECUTE IMMEDIATE 'truncate table ' ||
322       SYS.DBMS_ASSERT.ENQUOTE_NAME(p_tab_name);
323 
324   stmt := 'SELECT MIN(P_ID) FROM final_partition';
325   EXECUTE IMMEDIATE stmt into min_pid ;
326 
327   stmt := 'INSERT /*+ APPEND */ into ' ||
328     SYS.DBMS_ASSERT.ENQUOTE_NAME(p_tab_name) || ' (vertex_id,p_id,x,y) ' ||
329     ' select vertex_id, (p_id-:min_pid+1),x,y from final_partition';
330   EXECUTE IMMEDIATE stmt USING min_pid;
331 
332   COMMIT;
333 END adjust_final_pid;
334 
335 --
336 -- move vertices along the principal axes to make two partition with equal size
337 --
338 PROCEDURE make_partition_equal(tab_name IN VARCHAR2,
339                                 pid IN NUMBER,
340                                 v_no IN NUMBER,
341                                 part_counter IN NUMBER)
342 IS
343   no          INTEGER;
344   vno1        NUMBER;
345   vno2        NUMBER;
346   stmt        VARCHAR2(256);
347   part_m      NUMBER;
348 BEGIN
349   part_m := 0;
350 
351   -- two partitions for partition pid is based on the m sign (<0 or >= 0 )
352   stmt := 'SELECT COUNT(*) FROM ' ||
353     SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(tab_name) || ' WHERE m < 0 ';
354   EXECUTE IMMEDIATE stmt into vno1;
355 
356   vno2 := v_no - vno1;
357 
358   IF (vno1 > vno2) THEN
359     -- move n vertices from set (m < 0) to set (m>= 0)
360     -- by flipping its sign of m
361     no := (vno1-vno2)/2;
362     stmt := 'SELECT min(m) FROM (SELECT m FROM ' ||
363       SYS.DBMS_ASSERT.ENQUOTE_NAME(tab_name) ||
364         ' WHERE m < 0 ORDER BY m DESC)  ' || ' WHERE rownum <= :no ';
365     EXECUTE IMMEDIATE stmt into part_m USING no;
366 
367     INSERT /*+ APPEND */ INTO partition_tmp_3 (vertex_id,p_id,x,y,m)
368       SELECT vertex_id,
369           mdsys.SDO_ROUTER_PARTITION.get_pid(
370             mdsys.SDO_ROUTER_PARTITION.adjust_m(part_m,0,m),part_counter),
371           x,y,mdsys.SDO_ROUTER_PARTITION.adjust_m(part_m,0,m)
372         FROM partition_tmp_2;
373   ELSE
374     -- move n vertices from set (m >= 0) to set ( m < 0) by
375     -- updating pid in vertex table
376     no := (vno2-vno1)/2;
377     stmt := 'SELECT max(m) FROM (SELECT m FROM ' ||
378       SYS.DBMS_ASSERT.ENQUOTE_NAME(tab_name) ||
379         ' WHERE m >= 0 ORDER BY m)  ' || ' WHERE rownum <= :no ' ;
380     EXECUTE IMMEDIATE stmt into part_m USING no;
381 
382     INSERT /*+ APPEND */ INTO partition_tmp_3 (vertex_id,p_id,x,y,m)
383       SELECT vertex_id,
384               mdsys.SDO_ROUTER_PARTITION.get_pid(
385                 mdsys.SDO_ROUTER_PARTITION.adjust_m(0,part_m,m),part_counter),
386               x,y,mdsys.SDO_ROUTER_PARTITION.adjust_m(0,part_m,m)
387         FROM partition_tmp_2;
388   END IF;
389 
390   COMMIT;
391 
392 END make_partition_equal;
393 
394 --
395 -- partition procedure
396 --
397 PROCEDURE new_partition_proc(p_tab_name IN VARCHAR2,
398                               max_v_no IN NUMBER,
399                               partition_id IN NUMBER,
400                               make_equal IN BOOLEAN,
401                               part_counter IN OUT NUMBER)
402 IS
403   m_mean  NUMBER;
404   v_no    NUMBER;
405   x_mean  NUMBER;
406   y_mean  NUMBER;
407   eigenvec vector_2d;
408   partition_tmp_1 VARCHAR2(256);
409   stmt            VARCHAR2(256);
410   table_name      VARCHAR2(256);
411 BEGIN
412   --
413   -- terminal condition for bisecting
414   -- if vertex no. smaller than max_v_no ,stops
415 
416   IF (partition_id = 0) THEN
417     partition_tmp_1 := SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name);
418   ELSE
419     partition_tmp_1 := SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name) || '_' || partition_id;
420   END IF;
421 
422   stmt := 'SELECT COUNT(*) FROM ' || partition_tmp_1;
423   EXECUTE IMMEDIATE stmt into  v_no;
424 
425   IF (v_no = 0) THEN
426     RETURN;
427   END IF;
428 
429   --
430   -- prepare data for eigenvalue/eigenvector calculation
431   --
432 
436   stmt := 'SELECT mdsys.SDO_ROUTER_PARTITION.min_eigenvector(
433   stmt := 'SELECT AVG(x), AVG(y) from ' || SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(partition_tmp_1);
434   EXECUTE IMMEDIATE stmt into x_mean, y_mean;
435 
437                           sum(power(x-:1,2)), sum(power(y-:2,2)),
438                           sum((x-:3)*(y-:4))) FROM ' || partition_tmp_1;
439   EXECUTE IMMEDIATE stmt INTO eigenvec USING x_mean, y_mean, x_mean, y_mean;
440 
441   stmt := 'SELECT AVG(:1*(x - :2) + :3*(y-:4)) FROM ' || partition_tmp_1;
442   EXECUTE IMMEDIATE stmt INTO m_mean USING -eigenvec.y, x_mean, eigenvec.x, y_mean;
443 
444   stmt := 'INSERT /*+ APPEND */ into partition_tmp_2 (vertex_id,p_id,x,y,m)
445     SELECT vertex_id,p_id,x,y, (:1*(x - :2) + :3*(y-:4) - :5)
446       FROM ' || partition_tmp_1;
447   EXECUTE IMMEDIATE stmt USING -eigenvec.y, x_mean, eigenvec.x, y_mean, m_mean;
448 
449   COMMIT;
450 
451   --
452   -- make equal size if required
453   --
454 
455   IF (make_equal) THEN
456     make_partition_equal('partition_tmp_2',partition_id,v_no,part_counter);
457   ELSE
458     INSERT /*+ APPEND */ INTO partition_tmp_3 (vertex_id,p_id,x,y,m)
459       SELECT vertex_id,
460               mdsys.SDO_ROUTER_PARTITION.get_pid(m, part_counter),
461               x,y,m
462       FROM partition_tmp_2;
463 
464     COMMIT;
465   END IF;
466 
467   IF (partition_id = 0) THEN
468     EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || SYS.DBMS_ASSERT.ENQUOTE_NAME(p_tab_name);
469   ELSE
470     table_name := SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name) || '_' || partition_id;
471     EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;
472   END IF;
473 
474 
475   IF (v_no/2 > max_v_no) THEN
476 
477     table_name := SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name) || '_' || part_counter;
478     IF ( table_exists(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(table_name)) = 'TRUE') THEN
479             execute immediate 'DROP TABLE ' || SYS.DBMS_ASSERT.ENQUOTE_NAME(table_name);
480     END IF;
481 
482     stmt := 'CREATE  TABLE ' || table_name ||
483       ' STORAGE (maxextents unlimited), NOLOGGING as
484         SELECT * FROM partition_tmp_3 WHERE  p_id=' || part_counter;
485     EXECUTE IMMEDIATE stmt;
486 
487     table_name := SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name) || '_' || sdo_util.number_to_char(part_counter+1);
488 
489     IF ( table_exists(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(table_name)) = 'TRUE') THEN
490             execute immediate 'DROP TABLE ' || SYS.DBMS_ASSERT.ENQUOTE_NAME(table_name);
491     END IF;
492     stmt := 'CREATE TABLE ' || table_name ||
493       ' STORAGE (maxextents unlimited), NOLOGGING as
494       SELECT * FROM partition_tmp_3 WHERE p_id=' || sdo_util.number_to_char(part_counter+1);
495     EXECUTE IMMEDIATE stmt;
496 
497     COMMIT;
498   ELSE
499     INSERT /*+ APPEND */ INTO final_partition (vertex_id,p_id,x,y)
500      SELECT vertex_id,p_id,x,y FROM partition_tmp_3;
501 
502     COMMIT;
503   END IF;
504 
505   part_counter := part_counter+2;
506 
507   EXECUTE IMMEDIATE 'TRUNCATE TABLE partition_tmp_2';
508   EXECUTE IMMEDIATE 'TRUNCATE TABLE partition_tmp_3';
509 
510 END new_partition_proc;
511 
512 --
513 -- main pl/sql procedure to partition a graph with coordinate information
514 --
515 PROCEDURE graph_partition(p_tab_name IN VARCHAR2,
516                           max_v_no IN NUMBER,
517                           make_equal IN BOOLEAN)
518 IS
519   p_level   INTEGER;
520   stmt      VARCHAR2(256);
521   v_no      NUMBER;
522   min_pid   NUMBER;
523   max_pid   NUMBER;
524   pid       NUMBER;
525   p_counter NUMBER;
526   p_date    date;
527   show_time BOOLEAN := FALSE;
528 BEGIN
529   IF (table_exists(p_tab_name) = 'FALSE' ) THEN
530     log_message('ERROR: ' || p_tab_name || ' table not found');
531     RETURN ;
532   END IF;
533 
534   stmt := 'SELECT MIN(p_id), MAX(p_id) FROM ' ||
535     SYS.DBMS_ASSERT.ENQUOTE_NAME(p_tab_name);
536   EXECUTE IMMEDIATE stmt INTO min_pid, max_pid;
537 
538   stmt := 'SELECT COUNT(*) FROM ' ||
539     SYS.DBMS_ASSERT.ENQUOTE_NAME(p_tab_name) || ' WHERE p_id = :min_pid';
540   EXECUTE IMMEDIATE stmt INTO v_no USING  min_pid;
541 
542   p_level := floor(LN(v_no/max_v_no)/LN(2.0));
543 
544 
545   -- issue warning if the no of nodes in the table is smaller than max_v_no
546   if ( p_level < 0 ) THEN
547    log_message('WARNING: no. of nodes: ' || v_no || ' in table: ' || p_tab_name ||
548         ' is smaller than the given max_v_no: ' || max_v_no);
549    RETURN;
550   end if;
551 
552 
553   p_counter := max_pid+1; -- starting partition counter
554   pid := min_pid;
555 
556   log_message('INFO: begin partitioning of '|| p_tab_name ||
557     ' partition level: ' || p_level || ' min(partition id): ' ||
558       min_pid || ' max(partition id): ' || max_pid);
559 
560   log_message('INFO: generating ' || power(2,p_level+1) ||
561               ' partitions from level:0 to level: ' || p_level ||' ...', show_time);
562 
563   FOR k IN min_pid..max_pid LOOP
564     FOR i IN 0..p_level LOOP
565       p_date := sysdate;
566       FOR j IN 1..power(2,i) LOOP
567         new_partition_proc(SYS.DBMS_ASSERT.NOOP(p_tab_name), max_v_no, pid, make_equal, p_counter);
568         pid := pid +1;
569       END LOOP;
573       log_message('INFO:    partitioning level: ' || i || ' with ' || power(2,i+1) || ' partitions '|| ' took ' ||
570       log_message('INFO:    partitioning '|| p_tab_name ||
571         ' level: ' || i || ' partition id: ' || pid);
572       -- add computation time form each level
574                   sdo_util.number_to_char((sysdate-p_date)*24*60,'99999.999') || ' min.',show_time) ;
575     END LOOP;
576   END LOOP;
577 
578   -- Copy the result back to original table and ajust the pids
579   adjust_final_pid(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name));
580 
581   log_message('INFO: completed partitioning of '|| p_tab_name);
582 
583 EXCEPTION
584   WHEN OTHERS THEN
585    log_message('Exception processing partition '|| pid ||
586     ' of the ' || p_tab_name || ' table');
587    log_message(SQLERRM);
588    raise_application_error(-20009, 'Error Graphing Partitions');
589 
590 END graph_partition;
591 
592 --
593 -- drop all temporary tables for partitioning
594 --
595 PROCEDURE clean_tables (cleanup IN BOOLEAN DEFAULT TRUE)
596 
597 IS
598 BEGIN
599   -- Tables that should always be cleaned up
600   --
601   IF (table_exists('node_tmp') = 'TRUE') THEN
602     EXECUTE IMMEDIATE 'rename node_tmp to node';
603 
604     IF (constraint_exists('FK_EDGE_START_NODE_ID') = 'TRUE') THEN
605        EXECUTE IMMEDIATE 'ALTER TABLE edge DROP CONSTRAINT FK_EDGE_START_NODE_ID';
606     END IF;
607 
608     IF (constraint_exists('FK_EDGE_END_NODE_ID') = 'TRUE') THEN
609        EXECUTE IMMEDIATE 'ALTER TABLE edge DROP CONSTRAINT FK_EDGE_END_NODE_ID';
610     END IF;
611 
612     IF (constraint_exists('PK_NODE') = 'TRUE') THEN
613        EXECUTE IMMEDIATE 'ALTER TABLE node DROP CONSTRAINT PK_NODE';
614     END IF;
615 
616     IF (index_exists('node_id_index') = 'TRUE') THEN
617         EXECUTE IMMEDIATE 'DROP INDEX node_id_index';
618     END IF;
619     IF (index_exists('node_id_idx') = 'TRUE') THEN
620         EXECUTE IMMEDIATE 'DROP INDEX node_id_idx';
621     END IF;
622 
623     IF (index_exists('node_partition_index') = 'TRUE') THEN
624       EXECUTE IMMEDIATE 'DROP INDEX node_partition_index';
625     END IF;
626     IF (index_exists('node_partition_idx') = 'TRUE') THEN
627       EXECUTE IMMEDIATE 'DROP INDEX node_partition_idx';
628     END IF;
629 
630     -- rebuild node index
631     log_message('INFO: rebuilding node_id_idx on node');
632     EXECUTE IMMEDIATE 'CREATE INDEX node_id_idx on node(node_id)';
633 
634     -- rebuild primary key
635     EXECUTE IMMEDIATE 'ALTER TABLE node ADD CONSTRAINT pk_node PRIMARY KEY(node_id)';
636 
637     -- rebuild foreign keys
638     EXECUTE IMMEDIATE 'ALTER TABLE edge ADD CONSTRAINT fk_edge_start_node_id FOREIGN KEY (start_node_id) REFERENCES node(node_id)';
639     EXECUTE IMMEDIATE 'ALTER TABLE edge ADD CONSTRAINT fk_edge_end_node_id FOREIGN KEY (end_node_id) REFERENCES node(node_id)';
640 
641     -- rebuild node/partition index
642     log_message('INFO: rebuild node_partition_idx on node');
643     EXECUTE IMMEDIATE 'CREATE INDEX node_partition_idx on node(partition_id)';
644   END IF;
645 
646   IF (table_exists('edge_array_tmp') = 'TRUE') THEN
647     EXECUTE IMMEDIATE 'DROP TABLE edge_array_tmp';
648   END IF;
649 
650   IF (table_exists('final_partition') = 'TRUE') THEN
651     EXECUTE IMMEDIATE 'DROP TABLE final_partition';
652   END IF;
653 
654   IF (table_exists('partition_tmp_2') = 'TRUE') THEN
655     EXECUTE IMMEDIATE 'DROP TABLE partition_tmp_2';
656   END IF;
657 
658   IF (table_exists('partition_tmp_3') = 'TRUE') THEN
659     EXECUTE IMMEDIATE 'DROP TABLE partition_tmp_3';
660   END IF;
661 
662   -- Tables we may want to keep for debugging purposes.
663   --
664   IF (cleanup) THEN
665     IF (table_exists('edge_part') = 'TRUE') THEN
666       EXECUTE IMMEDIATE 'DROP TABLE edge_part';
667     END IF;
668 
669     IF (table_exists('node_part') = 'TRUE') THEN
670       EXECUTE IMMEDIATE 'DROP TABLE node_part';
671     END IF;
672 
673     IF (table_exists('restricted_nodes')= 'TRUE') THEN
674       EXECUTE IMMEDIATE 'DROP TABLE restricted_nodes';
675     END IF;
676 
677     IF (table_exists('restricted_edges')= 'TRUE') THEN
678       EXECUTE IMMEDIATE 'DROP TABLE restricted_edges';
679     END IF;
680 
681     IF (table_exists('super_edge_ids') = 'TRUE') THEN
682       EXECUTE IMMEDIATE 'DROP TABLE super_edge_ids';
683     END IF;
684 
685     IF (table_exists('super_node_ids') = 'TRUE') THEN
686       EXECUTE IMMEDIATE 'DROP TABLE super_node_ids';
687     END IF;
688     -- Trucking intermediate data
689     --
690     IF (table_exists('router_partitioned_truck_data') = 'TRUE') THEN
691       EXECUTE IMMEDIATE 'DROP TABLE router_partitioned_truck_data';
692     END IF;
693     IF (table_exists('partitioned_router_transport') = 'TRUE') THEN
694       EXECUTE IMMEDIATE 'DROP TABLE partitioned_router_transport';
695     END IF;
696     IF (table_exists('new_trucking_data') = 'TRUE') THEN
697       EXECUTE IMMEDIATE 'DROP TABLE new_trucking_data';
698     END IF;
699     -- Turn restriction intermediate data
700     IF (table_exists('partitioned_router_nav_strand') = 'TRUE') THEN
701       EXECUTE IMMEDIATE 'DROP TABLE partitioned_router_nav_strand';
702     END IF;
703     IF (table_exists('router_max_seq') = 'TRUE') THEN
707       EXECUTE IMMEDIATE 'DROP TABLE new_turn_restriction_data';
704       EXECUTE IMMEDIATE 'DROP TABLE router_max_seq';
705     END IF;
706     IF (table_exists('new_turn_restriction_data') = 'TRUE') THEN
708     END IF;
709   END IF;
710 END;
711 --
712 -- setup all temporary tables for partitioning
713 --
714 PROCEDURE setup_tables
715 IS
716 BEGIN
717   log_message('INFO: setting up partitioning temporary tables');
718 
719   IF (table_exists('partition_tmp_2') = 'TRUE') THEN
720     EXECUTE IMMEDIATE 'TRUNCATE TABLE partition_tmp_2';
721   ELSE
722    EXECUTE IMMEDIATE 'CREATE TABLE partition_tmp_2  (vertex_id NUMBER, p_id NUMBER, x NUMBER, y NUMBER,m NUMBER)
723                         STORAGE (maxextents unlimited), NOLOGGING';
724   END IF;
725 
726   IF (table_exists('partition_tmp_3') = 'TRUE') THEN
727     EXECUTE IMMEDIATE 'TRUNCATE TABLE partition_tmp_3';
728   ELSE
729    EXECUTE IMMEDIATE 'CREATE TABLE partition_tmp_3  (vertex_id NUMBER, p_id NUMBER, x NUMBER, y NUMBER,m NUMBER)
730                         STORAGE (maxextents unlimited), NOLOGGING';
731   END IF;
732 
733   IF (table_exists('final_partition')= 'TRUE') THEN
734     EXECUTE IMMEDIATE 'TRUNCATE TABLE final_partition';
735   ELSE
736     EXECUTE IMMEDIATE 'CREATE TABLE final_partition  (vertex_id NUMBER, p_id NUMBER, x NUMBER, y NUMBER)
737                         STORAGE (maxextents unlimited),  NOLOGGING';
738   END IF;
739 
740   IF (table_exists('edge_part') = 'TRUE') THEN
741     EXECUTE IMMEDIATE 'DROP TABLE edge_part';
742   END IF;
743 
744   IF (table_exists('node_part') = 'TRUE') THEN
745     EXECUTE IMMEDIATE 'DROP TABLE node_part';
746   END IF;
747 
748   IF (table_exists('restricted_nodes')= 'TRUE') THEN
749     EXECUTE IMMEDIATE 'DROP TABLE restricted_nodes';
750   END IF;
751 
752   IF (table_exists('restricted_edges')= 'TRUE') THEN
753     EXECUTE IMMEDIATE 'DROP TABLE restricted_edges';
754   END IF;
755 
756   IF (table_exists('super_edge_ids') = 'TRUE') THEN
757     EXECUTE IMMEDIATE 'DROP TABLE super_edge_ids';
758   END IF;
759 
760   IF (table_exists('super_node_ids') = 'TRUE') THEN
761     EXECUTE IMMEDIATE 'DROP TABLE super_node_ids';
762   END IF;
763 END setup_tables;
764 
765 
766 --
767 -- Create the node_part table needed for partitioning
768 --
769 PROCEDURE create_node_part
770 IS
771   stmt  VARCHAR2(256);
772 BEGIN
773   IF (table_exists('node_part') = 'TRUE') THEN
774     EXECUTE IMMEDIATE 'DROP TABLE node_part';
775   END IF;
776 
777   log_message('INFO: create and load node_part table');
778 
779   EXECUTE IMMEDIATE 'CREATE TABLE node_part(vertex_id NUMBER, x NUMBER, y NUMBER, p_id NUMBER, outedges mdsys.num_array, inedges mdsys.num_array)
780     STORAGE (maxextents unlimited), NOLOGGING';
781 
782   EXECUTE IMMEDIATE 'INSERT /*+ APPEND */ into node_part
783     SELECT n.node_id, n.geometry.sdo_point.x, n.geometry.sdo_point.y, 0, null, null
784       FROM node n';
785 
786   COMMIT;
787 
788   -- Create an index on the node_part table
789   --
790   log_message('INFO: create index np_vp_idx on node_part');
791   EXECUTE IMMEDIATE 'CREATE INDEX np_vp_idx on node_part(vertex_id, p_id)';
792 
793   -- gather table and index stats
794   log_message('INFO: gather node_part table statistics');
795   gather_table_stats('NODE_PART');
796 
797 END create_node_part;
798 
799 --
800 -- Create and load the restricted_edges table.
801 --
802 PROCEDURE create_restricted_edges
803 IS
804   TYPE CURSOR_TYPE IS REF CURSOR;
805   coords    MDSYS.SDO_ORDINATE_ARRAY;
806   divider   VARCHAR2(1);
807   edge_id   NUMBER;
808   ins_stmt  VARCHAR(256);
809   p_cursor  CURSOR_TYPE;
810 BEGIN
811   IF (table_exists('restricted_edges') = 'TRUE') THEN
812     EXECUTE IMMEDIATE 'DROP TABLE restricted_edges';
813   END IF;
814 
815   log_message('INFO: Create and load the restricted_edges table');
816 
817   EXECUTE IMMEDIATE 'CREATE TABLE restricted_edges(edge_id NUMBER,
818                                                    divider VARCHAR2(1),
819                                                    startx1 NUMBER, starty1 NUMBER,
820                                                    startx2 NUMBER, starty2 NUMBER,
821                                                    endx1 NUMBER, endy1 NUMBER,
822                                                    endx2 NUMBER, endy2 NUMBER)
823                        STORAGE (maxextents unlimited), NOLOGGING';
824 
825   ins_stmt := 'INSERT INTO restricted_edges VALUES ' ||
826     '(:eid, :div, :sx1, :sy1, :sx2, :sy2,:ex1, :ey1, :ex2, :ey2)';
827 
828   -- Find all edges attached to nodes that have restricted edges either inbound
829   -- or outbound from the node.
830   OPEN p_cursor FOR 'SELECT t.edge_id, t.divider, t.geometry.sdo_ordinates' ||
831     ' FROM edge t WHERE t.edge_id in ' ||
832       '(SELECT edge_id FROM EDGE WHERE start_node_id IN (SELECT node_id FROM restricted_nodes) UNION' ||
833       ' SELECT edge_id FROM EDGE WHERE end_node_id IN (SELECT node_id FROM restricted_nodes))';
834 
835   LOOP
836     FETCH p_cursor INTO edge_id, divider, coords;
837     EXIT WHEN p_cursor%NOTFOUND;
838 
839     -- Find and store the edges first and last segments
843       coords(3), coords(4),
840     EXECUTE IMMEDIATE ins_stmt USING
841       edge_id, divider,
842       coords(1), coords(2),
844       coords(coords.count-3), coords(coords.count-2),
845       coords(coords.count-1), coords(coords.count);
846   END LOOP;
847   CLOSE p_cursor;
848 
849   COMMIT;
850 
851   log_message('INFO: create index restricted_edges_idx');
852   EXECUTE IMMEDIATE 'CREATE INDEX restricted_edges_idx on restricted_edges(edge_id)';
853 
854   log_message('INFO: gather restricted_edges table statistics');
855   gather_table_stats('RESTRICTED_EDGES');
856 END create_restricted_edges;
857 
858 --
859 -- Create the restricted_nodes table needed for turn restriction generation
860 --
861 PROCEDURE create_restricted_nodes
862 IS
863 BEGIN
864   IF (table_exists('restricted_nodes') = 'TRUE') THEN
865     EXECUTE IMMEDIATE 'DROP TABLE restricted_nodes';
866   END IF;
867 
868   log_message('INFO: Create and load the restricted_nodes table');
869 
870   EXECUTE IMMEDIATE 'CREATE TABLE restricted_nodes(node_id NUMBER, inedges mdsys.num_array, outedges mdsys.num_array)
871      STORAGE (maxextents unlimited), NOLOGGING';
872 
873   -- Find all nodes that are the start or end point of a restricted edge.
874   EXECUTE IMMEDIATE 'INSERT /*+ APPEND */ INTO restricted_nodes
875     SELECT vertex_id, inedges, outedges FROM NODE_PART where vertex_id IN
876       (SELECT source_id FROM edge_part WHERE divider IN (''1'', ''A'') union
877        SELECT target_id FROM edge_part WHERE divider IN (''2'', ''A''))';
878 
879   COMMIT;
880 
881 END create_restricted_nodes;
882 
883 PROCEDURE update_node_part_edge_arrays
884 IS
885   TYPE CURSOR_TYPE IS REF CURSOR;
886   e_cursor CURSOR_TYPE;
887   e_id NUMBER;
888   e_array1 mdsys.num_array;
889   e_array2 mdsys.num_array;
890   n_cursor CURSOR_TYPE;
891   n_id NUMBER;
892   stmt  VARCHAR2(512);
893 BEGIN
894   -- Create a temporary table to store the results of the array builds
895   IF (table_exists('edge_array_tmp') = 'TRUE') THEN
896           EXECUTE IMMEDIATE 'DROP TABLE edge_array_tmp';
897   END IF;
898 
899   stmt := 'CREATE TABLE edge_array_tmp(vertex_id NUMBER, outedges mdsys.num_array, inedges mdsys.num_array)
900              STORAGE (maxextents unlimited), NOLOGGING';
901 
902   EXECUTE IMMEDIATE stmt;
903 
904   -- For every vertex in the node_part table build an in and out edge array.
905   -- The where clause forces a fast index only scan
906   OPEN n_cursor FOR 'SELECT vertex_id FROM node_part where vertex_id>0';
907   LOOP
908     FETCH n_cursor into n_id;
909       EXIT WHEN n_cursor%NOTFOUND;
910 
911     e_array1 := mdsys.num_array();
912 
913     -- Build an array of outedges for a particular node
914     OPEN e_cursor for 'select edge_id from edge_part where source_id=:id' using n_id;
915     LOOP
916       FETCH e_cursor INTO e_id;
917         EXIT WHEN e_cursor%NOTFOUND;
918 
919         e_array1.extend(1);
920         e_array1(e_array1.count) := e_id;
921     END LOOP;
922     CLOSE e_cursor;
923 
924     e_array2 := mdsys.num_array();
925 
926     -- Build an array of inedges for a particular node
927     OPEN e_cursor for 'select edge_id from edge_part where target_id=:id' using n_id;
928     LOOP
929       FETCH e_cursor INTO e_id;
930         EXIT WHEN e_cursor%NOTFOUND;
931 
932         e_array2.extend(1);
933         e_array2(e_array2.count) := e_id;
934     END LOOP;
935     CLOSE e_cursor;
936 
937     -- Store the results
938     stmt := 'INSERT INTO edge_array_tmp VALUES (:node_id, :outedges, :inedges)';
939     EXECUTE IMMEDIATE stmt USING n_id, e_array1, e_array2;
940   END LOOP;
941   CLOSE n_cursor;
942 
943   COMMIT;
944 
945   EXECUTE IMMEDIATE 'CREATE INDEX eat_idx on edge_array_tmp(vertex_id)';
946   gather_table_stats('EDGE_ARRAY_TMP');
947 
948   -- Rebuild the node_part table from scratch using the edge array information
949   stmt := 'CREATE TABLE new_node_part
950             STORAGE (maxextents unlimited), NOLOGGING AS
951               SELECT n.vertex_id, n.x, n.y, n.p_id, e.outedges, e.inedges
952               FROM node_part n, edge_array_tmp e
953               WHERE n.vertex_id=e.vertex_id';
954   EXECUTE IMMEDIATE stmt;
955 
956   -- Replace the old node_part table with the new one containing
957   -- the in and out edge arrays
958   --
959   EXECUTE IMMEDIATE 'DROP TABLE node_part';
960   EXECUTE IMMEDIATE 'RENAME new_node_part to node_part';
961 
962   -- Add needed indexes to the node_part table.
963   --
964   log_message('INFO: rebuild index np_vp_idx on node_part');
965   EXECUTE IMMEDIATE 'CREATE INDEX np_vp_idx on node_part(vertex_id, p_id)';
966 
967   log_message('INFO: create index node_part_p_idx on node_part');
968   EXECUTE IMMEDIATE 'CREATE INDEX node_part_p_idx on node_part(p_id)';
969 
970   -- rebuild the table and index stats
971   log_message('INFO: rebuild node_part table statistics');
972   gather_table_stats('NODE_PART');
973 
974   EXECUTE IMMEDIATE 'DROP TABLE edge_array_tmp';
975 END update_node_part_edge_arrays;
976 
977 --
978 -- Create the node_part table needed for partitioning
979 --
980 PROCEDURE create_super_tables
984   --
981 IS
982   stmt  VARCHAR2(256);
983 BEGIN
985   -- Drop any constraints on the EDGE and  NODE tables
986   IF (constraint_exists('FK_EDGE_START_NODE_ID') = 'TRUE') THEN
987       EXECUTE IMMEDIATE 'ALTER TABLE edge DROP CONSTRAINT FK_EDGE_START_NODE_ID';
988   END IF;
989   IF (constraint_exists('FK_EDGE_END_NODE_ID') = 'TRUE') THEN
990       EXECUTE IMMEDIATE 'ALTER TABLE edge DROP CONSTRAINT FK_EDGE_END_NODE_ID';
991   END IF;
992 
993   IF (constraint_exists('PK_NODE') = 'TRUE') THEN
994       EXECUTE IMMEDIATE 'ALTER TABLE node DROP CONSTRAINT PK_NODE';
995   ELSE
996       EXECUTE IMMEDIATE 'ALTER TABLE node DROP PRIMARY KEY';
997   END IF;
998 
999   -- Drop and indexes on the NODE table
1000   IF (index_exists('node_id_index') = 'TRUE') THEN
1001       EXECUTE IMMEDIATE 'DROP INDEX node_id_index';
1002   END IF;
1003   IF (index_exists('node_id_idx') = 'TRUE') THEN
1004       EXECUTE IMMEDIATE 'DROP INDEX node_id_idx';
1005   END IF;
1006 
1007   IF (index_exists('node_partition_index') = 'TRUE') THEN
1008       EXECUTE IMMEDIATE 'DROP INDEX node_partition_index';
1009   END IF;
1010   IF (index_exists('node_partition_idx') = 'TRUE') THEN
1011       EXECUTE IMMEDIATE 'DROP INDEX node_partition_idx';
1012   END IF;
1013 
1014   -- Rename the node table so we can use CTAS to rebuild it
1015   EXECUTE IMMEDIATE 'RENAME node to node_tmp';
1016 
1017   -- Create and populate the edge_part table.
1018   --
1019   IF (table_exists('edge_part') = 'TRUE') THEN
1020     EXECUTE IMMEDIATE 'DROP TABLE edge_part';
1021   END IF;
1022 
1023   log_message('INFO: create and load edge_part');
1024   EXECUTE IMMEDIATE 'CREATE TABLE edge_part(edge_id NUMBER, source_id NUMBER,
1025       target_id NUMBER, source_p_id NUMBER, target_p_id NUMBER,
1026       func_class NUMBER, length NUMBER, speed_limit NUMBER, divider VARCHAR2(1),
1027       turn_restrictions mdsys.num_array)
1028     STORAGE (maxextents unlimited), NOLOGGING';
1029 
1030   EXECUTE IMMEDIATE 'INSERT /*+ APPEND */ INTO edge_part
1031     SELECT edge_id, start_node_id, end_node_id,
1032         (SELECT p_id FROM node_part WHERE vertex_id = start_node_id),
1033         (SELECT p_id FROM node_part WHERE vertex_id = end_node_id),
1034         func_class, length, speed_limit, divider, null
1035       FROM edge';
1036 
1037   COMMIT;
1038 
1039   -- Create useful indices on edge_part.
1040   --
1041   log_message('INFO: create index edge_part_e_idx on edge_part');
1042   EXECUTE IMMEDIATE 'CREATE INDEX edge_part_e_idx on edge_part(edge_id)';
1043 
1044   log_message('INFO: create index edge_part_s_e_idx on edge_part');
1045   EXECUTE IMMEDIATE 'CREATE INDEX edge_part_s_e_idx on edge_part(source_id, edge_id)';
1046 
1047   log_message('INFO: create index edge_part_t_e_idx on edge_part');
1048   EXECUTE IMMEDIATE 'CREATE INDEX edge_part_t_e_idx on edge_part(target_id, edge_id)';
1049 
1050   log_message('INFO: create index edge_part_st_p_idx on edge_part');
1051   EXECUTE IMMEDIATE 'CREATE INDEX edge_part_st_p_idx on edge_part(source_p_id, target_p_id)';
1052 
1053   log_message('INFO: create index edge_part_ts_p_idx on edge_part');
1054   EXECUTE IMMEDIATE 'CREATE INDEX edge_part_ts_p_idx on edge_part(target_p_id, source_p_id)';
1055 
1056   -- build the table and index stats
1057   log_message('INFO: gather edge_part table statistics');
1058   gather_table_stats('EDGE_PART');
1059 
1060   -- Populate the inedges and outedges fields to the node_part table.
1061   --
1062   log_message('INFO: create and load outedge and inedge columns in node_part table');
1063   update_node_part_edge_arrays;
1064 
1065   create_restricted_nodes;
1066 
1067   -- Recreate the node table and load it from the node_tmp table.
1068   --
1069   log_message('INFO: recreating node table with partitioning information');
1070   EXECUTE IMMEDIATE 'CREATE TABLE node STORAGE (maxextents unlimited), NOLOGGING AS
1071                       SELECT nt.node_id, nt.geometry, np.p_id partition_id
1072                       FROM node_tmp nt, node_part np
1073                       WHERE nt.node_id = np.vertex_id';
1074   COMMIT;
1075 
1076   EXECUTE IMMEDIATE 'DROP TABLE node_tmp CASCADE CONSTRAINTS';
1077 
1078   -- Create an index on the node and partition id fields in the NODE table.
1079   --
1080   log_message('INFO: create index node_id_idx on node');
1081   EXECUTE IMMEDIATE 'CREATE INDEX node_id_idx on node(node_id)';
1082 
1083   -- Make node_id a Primary key
1084   EXECUTE IMMEDIATE 'ALTER TABLE node ADD CONSTRAINT pk_node PRIMARY KEY(node_id)';
1085 
1086   log_message('INFO: create foreign keys');
1087   EXECUTE IMMEDIATE 'ALTER TABLE edge ADD CONSTRAINT fk_edge_start_node_id FOREIGN KEY (start_node_id) REFERENCES node(node_id)';
1088   EXECUTE IMMEDIATE 'ALTER TABLE edge ADD CONSTRAINT fk_edge_end_node_id FOREIGN KEY (end_node_id) REFERENCES node(node_id)';
1089 
1090   log_message('INFO: create index node_partition_idx on node');
1091   EXECUTE IMMEDIATE 'CREATE INDEX node_partition_idx on node(partition_id)';
1092 
1093   log_message('INFO: rebuild node table statistics');
1094   gather_table_stats('NODE');
1095 
1096   -- Set the partition ids in the edge table.
1097   --
1098   EXECUTE IMMEDIATE 'ALTER TABLE edge nologging';
1099   IF (index_exists('edge_partition_index') = 'TRUE') THEN
1100       EXECUTE IMMEDIATE 'DROP INDEX edge_partition_index';
1101   END IF;
1102 
1106                           WHERE  vertex_id = start_node_id)';
1103   log_message('INFO: updating edge table with partitioning information');
1104   EXECUTE IMMEDIATE 'UPDATE edge
1105     SET partition_id = (SELECT p_id from node_part
1107   COMMIT;
1108 
1109   -- Create an index on the partition id field in the EDGE table.
1110   --
1111   log_message('INFO: create index edge_partition_index on edge');
1112   EXECUTE IMMEDIATE 'CREATE INDEX edge_partition_index on edge(partition_id)';
1113 
1114   -- build the table and index stats
1115   log_message('INFO: gather edge table statistics');
1116   gather_table_stats('EDGE');
1117 
1118   -- Create and populate the super_node and super_edge tables.
1119   --
1120   log_message('INFO: creating and loading super_node_ids table');
1121   IF (table_exists('super_node_ids') = 'TRUE') THEN
1122     EXECUTE IMMEDIATE 'TRUNCATE TABLE super_node_ids';
1123   ELSE
1124     EXECUTE IMMEDIATE 'CREATE TABLE super_node_ids (node_id number)';
1125   END IF;
1126 
1127   EXECUTE IMMEDIATE 'INSERT /*+APPEND */ into super_node_ids
1128     (SELECT source_id FROM edge_part WHERE func_class = 1 or func_class=2
1129       UNION
1130     SELECT target_id FROM edge_part WHERE func_class = 1 or func_class=2)';
1131 
1132   log_message('INFO: creating and loading super_edge_ids table');
1133   IF (table_exists('super_edge_ids') = 'TRUE') THEN
1134     EXECUTE IMMEDIATE 'TRUNCATE TABLE super_edge_ids';
1135   ELSE
1136     EXECUTE IMMEDIATE 'CREATE TABLE super_edge_ids (edge_id number)';
1137   END IF;
1138 
1139   EXECUTE IMMEDIATE 'INSERT /*+APPEND */ into super_edge_ids
1140     SELECT edge_id FROM edge_part WHERE func_class =1 or func_class = 2';
1141 
1142   COMMIT;
1143 END create_super_tables;
1144 
1145 ---
1146 ---
1147 ---
1148 PROCEDURE gather_table_stats(table_name IN VARCHAR2)
1149 IS
1150   stmt  VARCHAR2(256):= 'EXECUTE DBMS_STATS.GATHER_TABLE_STATS(:schema_name, :table_name, NULL, DBMS_STATS.AUTO_SAMPLE_SIZE)';
1151 BEGIN
1152   IF (table_exists(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(table_name)) = 'FALSE') THEN
1153     log_message('ERROR: ' || table_name || ' not found for gather table statistics');
1154     RETURN ;
1155   END IF;
1156 
1157   IF (schema_name IS NULL) THEN
1158      stmt := 'SELECT username FROM user_users';
1159      EXECUTE IMMEDIATE stmt into schema_name;
1160   END IF;
1161 
1162   -- Gather table and index stats for the specified table
1163   DBMS_STATS.GATHER_TABLE_STATS(
1164     ownname => schema_name,
1165     tabname => table_name,
1166     estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
1167     degree => DBMS_STATS.AUTO_DEGREE);
1168 
1169 END gather_table_stats;
1170 
1171 ---
1172 ---
1173 ---
1174 FUNCTION get_edge_info(edge_ids       IN  sdo_list_type,
1175                        to_edge_ids    OUT sdo_list_type,
1176                        rets           OUT mdsys.string_array,
1177                        angle_segments OUT sdo_list_type)
1178 RETURN mdsys.string_array AS
1179     n           INTEGER;
1180     i           INTEGER;
1181     k           INTEGER;
1182     base        INTEGER := 1;
1183     to_edge_id  INTEGER;
1184     coords      mdsys.sdo_ordinate_array;
1185     names       mdsys.string_array;
1186     name_query  VARCHAR2(2000);
1187     sign_query  VARCHAR2(2000);
1188     ret         VARCHAR2(200);
1189     TYPE cursor_type IS REF CURSOR;
1190     sign_cursor cursor_type;
1191 BEGIN
1192     IF (edge_ids IS NULL) THEN
1193         RETURN NULL;
1194     END IF;
1195 
1196     -- Initialize varrays
1197     n := edge_ids.count;
1198     names := mdsys.string_array();
1199     to_edge_ids := sdo_list_type();
1200     rets := mdsys.string_array();
1201     angle_segments := sdo_list_type();
1202     names.extend(n);
1203     to_edge_ids.extend(n);
1204     rets.extend(n);
1205     -- Need 4 points to describe a start and end segment for each edge id
1206     angle_segments.extend(n*8);
1207 
1208     -- Initialize name query
1209     name_query := 'SELECT t.name,t.geometry.sdo_ordinates FROM edge t WHERE t.edge_id = :1';
1210     -- Initialize sign query
1211     sign_query := 'SELECT to_edge_id, ' ||
1212                   'ramp || '':'' || exit || '':'' || toward '||
1213                   'FROM sign_post ' ||
1214                   'WHERE from_edge_id = :1';
1215 
1216 
1217     -- Iterate through route edge_ids and find info for each.
1218     FOR i IN 1..n LOOP
1219         EXECUTE IMMEDIATE name_query
1220         INTO names(i), coords
1221         USING edge_ids(i);
1222         IF (names(i) IS NULL) THEN
1223             names(i) := 'RAMP';
1224         END IF;
1225         -- Get sign information, if any.
1226         to_edge_ids(i) := 0;
1227         rets(i) := NULL;
1228         -- Get the coordinates for the start and end segments of the edge
1229         angle_segments(base)   := coords(1);
1230         angle_segments(base+1) := coords(2);
1231         angle_segments(base+2) := coords(3);
1232         angle_segments(base+3) := coords(4);
1233         angle_segments(base+4) := coords(coords.count-3);
1234         angle_segments(base+5) := coords(coords.count-2);
1235         angle_segments(base+6) := coords(coords.count-1);
1236         angle_segments(base+7) := coords(coords.count);
1237         base := base + 8;
1238         -- We have to use ABS(edge_ids(i)) since sign_post
1242         OPEN sign_cursor FOR sign_query USING ABS(edge_ids(i));
1239         -- table contains only NAVSTREETS edge ids
1240         -- (positive only) not routeserver edge ids (which
1241         -- can be negative).
1243         LOOP
1244             FETCH sign_cursor INTO to_edge_id, ret;
1245             EXIT WHEN sign_cursor%NOTFOUND;
1246             FOR k IN i+1..n LOOP
1247                 IF (to_edge_id = edge_ids(k) OR
1248                     (-1*to_edge_id) = edge_ids(k)) THEN
1249                     -- Make sure we assign router edge id:
1250                     -- (negative or positive)!!!
1251                     to_edge_ids(i) := edge_ids(k);
1252                     rets(i) := ret;
1253                     EXIT;
1254                 END IF;
1255             END LOOP;
1256             IF (to_edge_ids(i) <> 0) THEN
1257                 EXIT;
1258             END IF;
1259         END LOOP;
1260         CLOSE sign_cursor;
1261     END LOOP;
1262     RETURN names;
1263 END get_edge_info;
1264 
1265 ---
1266 ---
1267 ---
1268 FUNCTION get_geometry_info (edge_ids      IN  sdo_list_type,
1269                             merged_coords OUT sdo_list_type)
1270 RETURN NUMBER AS
1271   coords            MDSYS.SDO_ORDINATE_ARRAY;
1272   j                 NUMBER;
1273   k                 NUMBER;
1274 BEGIN
1275   IF (edge_ids IS NULL) THEN
1276     RETURN 0;
1277   END IF;
1278 
1279   k := 1;
1280   merged_coords := sdo_list_type();
1281 
1282   -- For each input edge id, get the list of coordinates for the edge and
1283   -- build a list of all coordinates for the edges.
1284   FOR i in 1 .. edge_ids.count
1285   LOOP
1286     EXECUTE IMMEDIATE
1287      'select t.geometry.sdo_ordinates from edge t ' ||
1288      'where edge_id=:i'
1289     INTO coords USING edge_ids(i);
1290 
1291     j := 1;
1292 
1293     merged_coords.extend(coords.count + 1);
1294     merged_coords(k) := coords.count;
1295     k := k + 1;
1296 
1297     WHILE j <= coords.count
1298     LOOP
1299       merged_coords(k) := coords(j);
1300       merged_coords(k+1) := coords(j+1);
1301 
1302       j := j + 2;
1303       k := k + 2;
1304     END LOOP;
1305   END LOOP;
1306 
1307   RETURN merged_coords.count;
1308 
1309 END get_geometry_info;
1310 
1311 ---
1312 --- Find and validate the Routeservers data version
1313 ---
1314 FUNCTION get_version_info
1315   RETURN VARCHAR2
1316 IS
1317   data_version  VARCHAR2(32) := '10.2.0.4.0';
1318   major_version VARCHAR2(16);
1319   stmt          VARCHAR2(256);
1320   v_count       NUMBER;
1321 BEGIN
1322   IF (table_exists('SDO_ROUTER_DATA_VERSION')= 'TRUE') THEN
1323     stmt := 'SELECT COUNT(*) FROM SDO_ROUTER_DATA_VERSION';
1324     EXECUTE IMMEDIATE stmt INTO v_count;
1325 
1326     IF (v_count != 1) THEN
1327       log_message('ERROR: Routeserver data version table corrupted, ' ||
1328         'multiple versions found');
1329       raise_application_error(-20005, 'Error getting data version, multiple versions found');
1330     END IF;
1331 
1332     stmt := 'SELECT data_version FROM SDO_ROUTER_DATA_VERSION';
1333     EXECUTE IMMEDIATE stmt INTO data_version;
1334 
1335     major_version := SUBSTR(data_version, 0, INSTR(data_version, '.')-1);
1336 
1337     IF (INSTR(data_version, '.', 1, 4) = 0 OR
1338         (major_version!='12' AND major_version!='11' AND major_version!='10')) THEN
1339       log_message('ERROR: Routeserver data version table corrupted, ' ||
1340         'unsupported data version ' || data_version);
1341       raise_application_error(-20005,
1342         'Error getting data version, unsupported data version ' || data_version);
1343     END IF;
1344   END IF;
1345 
1346   RETURN data_version;
1347 END get_version_info;
1348 
1349 ---
1350 ---
1351 ---
1352 FUNCTION is_10g(version IN VARCHAR2)
1353   RETURN BOOLEAN
1354 IS
1355 BEGIN
1356   RETURN (SUBSTR(version, 0, INSTR(version, '.')-1) = '10');
1357 END;
1358 
1359 --
1360 -- Entry point to used to create a network on the Router data
1361 --
1362 PROCEDURE create_router_network(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
1363                                 network_name  IN VARCHAR2 := 'ROUTER_NETWORK')
1364 IS
1365   expression      VARCHAR2(1024);
1366   full_file_name  VARCHAR2(256);
1367   l_network_name  VARCHAR2(256);
1368   link_view_name  VARCHAR2(256);
1369   node_view_name  VARCHAR2(256);
1370   part_view_name  VARCHAR2(256);
1371   pblob_view_name VARCHAR2(256);
1372   stmt            VARCHAR2(1024);
1373 BEGIN
1374   full_file_name := open_log_file(log_file_name);
1375 
1376   -- Sanity check the passed in network name
1377   l_network_name := nls_upper(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(network_name));
1378 
1379   -- Build the view names based on the network names
1380   link_view_name := l_network_name || '_LINK$';
1381   node_view_name := l_network_name || '_NODE$';
1382   part_view_name := l_network_name || '_PART$';
1383   pblob_view_name := l_network_name || '_PBLOB$';
1384 
1385   log_message('INFO: creating the Routeserver network: ' || l_network_name);
1386 
1387   -- if the network already exists, delete it first
1388   IF (network_exists(l_network_name) = 'TRUE') THEN
1392   -- create index for edge func_class
1389      delete_router_network(log_file_name, l_network_name, FALSE);
1390   END IF;
1391 
1393   IF (index_exists('EDGE_FUNC_CLASS_IDX') = 'FALSE') THEN
1394     log_message('      creating function class index', FALSE);
1395     EXECUTE IMMEDIATE 'create index EDGE_FUNC_CLASS_IDX on EDGE (FUNC_CLASS)';
1396   END IF;
1397 
1398   -- create a function based index for link level
1399   IF (index_exists('EDGE_LEVEL_IDX') = 'FALSE') THEN
1400     log_message('      creating link level index', FALSE);
1401     EXECUTE IMMEDIATE 'create index EDGE_LEVEL_IDX on EDGE(elocation_edge_link_level(FUNC_CLASS))';
1402   ELSE
1403     stmt := 'SELECT COLUMN_EXPRESSION FROM USER_IND_EXPRESSIONS WHERE INDEX_NAME = :name';
1404     EXECUTE IMMEDIATE stmt into expression using 'EDGE_LEVEL_IDX';
1405 
1406     IF(substr(expression, 10, 25) <> 'ELOCATION_EDGE_LINK_LEVEL') THEN
1407       log_message('      dropping current link level index', FALSE);
1408       EXECUTE IMMEDIATE 'DROP INDEX EDGE_LEVEL_IDX';
1409 
1410       log_message('      creating link level index', FALSE);
1411       EXECUTE IMMEDIATE 'create index EDGE_LEVEL_IDX on EDGE(elocation_edge_link_level(FUNC_CLASS))';
1412     END IF;
1413   END IF;
1414 
1415   -- rebuild the table and index stats
1416   log_message('INFO: rebuild edge table statistics');
1417   gather_table_stats('EDGE');
1418 
1419   log_message('      creating views', FALSE);
1420 
1421   -- create a view on the NODE table
1422   stmt:=
1423     'create or replace view ' || node_view_name ||
1424         ' as select n.node_id node_id,
1425                n.geometry geometry,
1426                n.geometry.sdo_point.x x,
1427                n.geometry.sdo_point.y y
1428            from NODE n';
1429 
1430   EXECUTE IMMEDIATE stmt;
1431 
1432   -- create a view on the EDGE table
1433   stmt :=
1434     'create or replace view ' || link_view_name ||
1435         ' as select edge_id link_id,
1436               start_node_id start_node_id,
1437               end_node_id end_node_id,
1438               elocation_edge_link_level(FUNC_CLASS) link_level,
1439               length length,
1440               speed_limit s,
1441               func_class f,
1442               geometry geometry,
1443               name name,
1444               divider divider
1445             from EDGE';
1446 
1447   EXECUTE IMMEDIATE stmt;
1448 
1449   --create a view on the NODE table node_id and partition_id information
1450   stmt :=
1451     'create or replace view ' || part_view_name ||
1452         ' as select node_id node_id,
1453               partition_id partition_id,
1454               1 link_level
1455             from NODE';
1456 
1457   EXECUTE IMMEDIATE stmt;
1458 
1459   -- create a view on the PARTITION table adding link level and changing
1460   -- the format of the edge counts
1461   stmt := 'create or replace view ' || pblob_view_name ||
1462       ' as select link_level link_level,
1463             a.partition_id partition_id,
1464             subnetwork blob,
1465             num_nodes num_inodes,
1466             num_outgoing_boundary_edges+num_incoming_boundary_edges num_enodes,
1467             num_non_boundary_edges num_ilinks,
1468             num_outgoing_boundary_edges+num_incoming_boundary_edges num_elinks,
1469             num_incoming_boundary_edges num_inlinks,
1470             num_outgoing_boundary_edges num_outlinks, ' ||
1471             SYS.DBMS_ASSERT.ENQUOTE_LITERAL('Y') || ' user_data_included
1472            from
1473             (select 1 link_level, partition_id partition_id
1474              from PARTITION
1475              where partition_id > 0
1476               union all
1477              select 2 link_level, partition_id partition_id
1478              from PARTITION
1479              where partition_id = 0) a,
1480             PARTITION b
1481            where a.partition_id = b.partition_id';
1482 
1483   EXECUTE IMMEDIATE stmt;
1484 
1485   log_message('      generating metadata', FALSE);
1486 
1487   --insert network metadata
1488   stmt := 'insert into USER_SDO_NETWORK_METADATA
1489             (network,
1490              network_category,
1491              geometry_type,
1492              node_table_name,
1493              node_geom_column,
1494              link_table_name,
1495              link_geom_column,
1496              link_cost_column,
1497              link_direction,
1498              partition_table_name,
1499              partition_blob_table_name,
1500              user_defined_data)
1501            values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12)';
1502 
1503   EXECUTE IMMEDIATE stmt USING l_network_name, 'SPATIAL', 'SDO_GEOMETRY',
1504     node_view_name, 'GEOMETRY', link_view_name, 'GEOMETRY', 'LENGTH',
1505     'DIRECTED', part_view_name, pblob_view_name, 'Y';
1506 
1507   -- insert user data metadata
1508   -- node x coordinate
1509   stmt := 'insert into user_sdo_network_user_data
1510             (network, table_type, data_name,data_type)
1511            values (:1, :2, :3, :4)';
1512   EXECUTE IMMEDIATE stmt USING l_network_name, 'NODE', 'X', 'NUMBER';
1513 
1514   -- node y coordinate
1515   stmt := 'insert into user_sdo_network_user_data
1516             (network, table_type, data_name, data_type)
1517            values (:1, :2, :3, :4)';
1521   stmt := 'insert into user_sdo_network_user_data
1518   EXECUTE IMMEDIATE stmt USING l_network_name, 'NODE', 'Y', 'NUMBER';
1519 
1520   -- link speed limit
1522             (network, table_type, data_name, data_type)
1523            values (:1, :2, :3, :4)';
1524   EXECUTE IMMEDIATE stmt USING l_network_name, 'LINK', 'S', 'NUMBER';
1525 
1526   -- link function class
1527   stmt := 'insert into user_sdo_network_user_data
1528             (network, table_type, data_name, data_type)
1529            values (:1, :2, :3, :4)';
1530   EXECUTE IMMEDIATE stmt USING l_network_name, 'LINK', 'F', 'NUMBER';
1531 
1532   COMMIT;
1533 
1534   utl_file.fclose(part_log_file);
1535 END;
1536 
1537 --
1538 -- Entry point to used to create a network on the Router data
1539 --
1540 PROCEDURE delete_router_network(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
1541                                 network_name  IN VARCHAR2 := 'ROUTER_NETWORK',
1542                                 log_delete IN BOOLEAN := TRUE)
1543 IS
1544   full_file_name  VARCHAR2(256);
1545   l_network_name  VARCHAR2(256);
1546   link_view_name  VARCHAR2(256);
1547   node_view_name  VARCHAR2(256);
1548   part_view_name  VARCHAR2(256);
1549   pblob_view_name VARCHAR2(256);
1550   stmt            VARCHAR2(512);
1551 BEGIN
1552   full_file_name := open_log_file(log_file_name);
1553   l_network_name := nls_upper(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(network_name));
1554 
1555   -- raise an error if the network doesn't exist
1556   IF (network_exists(l_network_name) = 'FALSE') THEN
1557     log_message('ERROR: network delete failed, ' || l_network_name || ' network not found');
1558     raise_application_error(-20020, 'Network delete failed, ' || l_network_name || ' network not found');
1559   END IF;
1560 
1561   IF (log_delete) THEN
1562     log_message('INFO: deleting the Routeserver network: ' || l_network_name);
1563   ELSE
1564     log_message('Ping');
1565   END IF;
1566 
1567   stmt := 'select node_table_name, link_table_name,
1568                   partition_table_name, partition_blob_table_name
1569            from USER_SDO_NETWORK_METADATA
1570            where NETWORK = :name';
1571 
1572   EXECUTE IMMEDIATE stmt
1573     INTO node_view_name, link_view_name, part_view_name, pblob_view_name
1574     USING l_network_name;
1575 
1576   -- cleanup metadata
1577   stmt := 'delete from USER_SDO_NETWORK_METADATA where NETWORK = :name';
1578   EXECUTE IMMEDIATE stmt using l_network_name;
1579 
1580   stmt := 'delete from USER_SDO_NETWORK_USER_DATA where NETWORK = :name';
1581   EXECUTE IMMEDIATE stmt using l_network_name;
1582 
1583   -- cleanup views
1584   IF (table_exists(link_view_name) = 'TRUE') THEN
1585       EXECUTE IMMEDIATE 'DROP VIEW ' ||
1586         SYS.DBMS_ASSERT.ENQUOTE_NAME(link_view_name);
1587   END IF;
1588 
1589   IF (table_exists(node_view_name) = 'TRUE') THEN
1590       EXECUTE IMMEDIATE 'DROP VIEW ' ||
1591         SYS.DBMS_ASSERT.ENQUOTE_NAME(node_view_name);
1592   END IF;
1593 
1594   IF (table_exists(part_view_name) = 'TRUE') THEN
1595       EXECUTE IMMEDIATE 'DROP VIEW ' ||
1596         SYS.DBMS_ASSERT.ENQUOTE_NAME(part_view_name);
1597   END IF;
1598 
1599   IF (table_exists(pblob_view_name) = 'TRUE') THEN
1600       EXECUTE IMMEDIATE 'DROP VIEW ' ||
1601         SYS.DBMS_ASSERT.ENQUOTE_NAME(pblob_view_name);
1602   END IF;
1603 
1604   COMMIT;
1605 
1606   utl_file.fclose(part_log_file);
1607 END;
1608 
1609 --
1610 -- Entry point and driver of the entire partitioning process.
1611 -- high level procedure for partitioning graph based on coordinate
1612 -- information (inertia bisecting). The parameters are defaulted
1613 -- so the customers don't have to worry about them.
1614 --
1615 PROCEDURE partition_router(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
1616                         max_v_no IN NUMBER DEFAULT 10000,
1617                         driving_side IN VARCHAR2 := 'R',
1618                         network_name IN VARCHAR := 'ROUTER_NETWORK',
1619                         max_memory IN NUMBER := 1.75,
1620                         cleanup IN BOOLEAN DEFAULT TRUE,
1621                         use_securefiles IN BOOLEAN DEFAULT TRUE,
1622                         generate_11g_restrictions IN BOOLEAN DEFAULT TRUE)
1623 IS
1624   full_file_name VARCHAR2(256);
1625   max_memory_in_bytes NUMBER := (max_memory*1073741824);
1626   stmt         VARCHAR2(256);
1627   msg_cleanup  VARCHAR2(10) := 'TRUE';
1628   msg_blob_format VARCHAR2(16) := 'SECUREFILE';
1629   msg_restrictions VARCHAR2(10) := 'TRUE';
1630 BEGIN
1631   IF (NOT cleanup) THEN
1632     msg_cleanup := 'FALSE';
1633   END IF;
1634 
1635   IF (NOT use_securefiles) THEN
1636     msg_blob_format := 'BASICFILE';
1637   END IF;
1638 
1639   IF (NOT generate_11g_restrictions) THEN
1640     msg_restrictions := 'FALSE';
1641   END IF;
1642 
1643   full_file_name := open_log_file(log_file_name);
1644 
1645   -- Query the schema name for gathering statistics
1646   stmt := 'SELECT username FROM user_users';
1647   EXECUTE IMMEDIATE stmt into schema_name;
1648 
1649   log_message('******** Beginning SDO Router partitioning');
1650   log_message('** Schema: ' || schema_name, FALSE);
1651   log_message('** Logfile location: ' || full_file_name, FALSE);
1652   log_message('** Nodes per partition: ' || max_v_no, FALSE);
1656   log_message('** Cleanup temporary files: ' || msg_cleanup, FALSE);
1653   log_message('** Driving side: ' || driving_side, FALSE);
1654   log_message('** Router network name: ' || network_name, FALSE);
1655   log_message('** Max JVM Memory Size: ' || max_memory || 'GB (' || max_memory_in_bytes || ' bytes)', FALSE);
1657   log_message('** BLOBs stored in ' || msg_blob_format || ' format' , FALSE);
1658   log_message('** Generating 11g turn restrictions: ' || msg_restrictions, FALSE);
1659 
1660   setup_tables;
1661 
1662   create_node_part;
1663 
1664   graph_partition('NODE_PART', max_v_no, TRUE);
1665 
1666   create_super_tables;
1667 
1668   IF (generate_11g_restrictions) THEN
1669     -- Table of nodes that are either the start or end node of a restricted edge.
1670     create_restricted_nodes;
1671 
1672     -- Table of edges that that have either a start or end node in the
1673     -- restricted_nodes table. We store the first and last segment of each edge.
1674     -- These segments are far more accurate than the edge as a whole for
1675     -- computing turn angles.
1676     create_restricted_edges;
1677   END IF;
1678 
1679   -- Close the log file so the Java code can use it
1680   utl_file.fclose(part_log_file);
1681 
1682   -- Adjust the Oracle JVM maximum memory size to 1.5g.
1683   -- This is the equivelent to specifying -Xmx1536m to
1684   -- java outside the database. Memory size is specified in bytes.
1685   ElocationSetJVMHeapSize(max_memory_in_bytes);
1686 
1687   IF (generate_11g_restrictions) THEN
1688     -- Generate the 11g turn restrictions for compatability.
1689     -- This MUST be done after the NODE_PART and EDGE_PART tables have
1690     -- been populated and before the creation of the partition table.
1691     build_turn_restrictions(full_file_name, driving_side);
1692   END IF;
1693 
1694 BEGIN
1695   -- Java code to create the partiton table
1696   elocation_partition_router(full_file_name, msg_blob_format);
1697 EXCEPTION
1698   WHEN OTHERS THEN
1699     part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
1700     log_message(SQLERRM);
1701     utl_file.fclose(part_log_file);
1702     RAISE JAVA_ERROR;
1703 END;
1704 
1705   -- Reopen the logfile
1706   part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
1707 
1708   log_message('INFO: creating the final partition table');
1709 
1710   -- Rename the new partition table and build an index on it.
1711   IF (table_exists('partition')= 'TRUE') THEN
1712     EXECUTE IMMEDIATE 'DROP TABLE partition';
1713   END IF;
1714 
1715   EXECUTE IMMEDIATE 'RENAME new_partition to partition';
1716 
1717   log_message('INFO: create index partition_p_idx on partition table');
1718   EXECUTE IMMEDIATE 'CREATE INDEX partition_p_idx on partition(partition_id)';
1719 
1720   log_message('INFO: gather partition table statistics');
1721   gather_table_stats('PARTITION');
1722 
1723   -- Create the indexes, views and metadata needed by the NDM on top of
1724   -- the Router data
1725   create_router_network(log_file_name, network_name);
1726 
1727   -- Generate user data based turn restrictions.
1728   -- This must be done after the NODE_PART and EDGE_PART tables
1729   create_turn_restriction_data(log_file_name, cleanup);
1730 
1731   -- If trucking user data exists, partition it.
1732   IF (table_exists('router_transport') = 'TRUE') THEN
1733     create_trucking_data(log_file_name, TRUE);
1734   END IF;
1735 
1736   -- Reopen the logfile
1737   part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
1738   clean_tables(cleanup);
1739   log_message('******** Completed SDO Router partitioning');
1740 
1741   -- Close the log file
1742   utl_file.fclose(part_log_file);
1743 EXCEPTION
1744     WHEN JAVA_ERROR THEN
1745         raise_application_error(-20000, 'Oracle Router partitioning failed');
1746     WHEN OTHERS THEN
1747         IF (utl_file.is_open(part_log_file) = FALSE) THEN
1748           part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
1749         END IF;
1750         log_message(SQLERRM);
1751         utl_file.fclose(part_log_file);
1752         raise_application_error(-20000, 'Oracle Router partitioning failed');
1753 END partition_router;
1754 
1755 --
1756 -- Entry point to used to produce a dump of the partition BLOBs
1757 --
1758 PROCEDURE dump_partitions(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
1759                       start_pid IN NUMBER DEFAULT 0,
1760                       end_pid IN NUMBER DEFAULT -1,
1761                       verbose IN BOOLEAN DEFAULT FALSE)
1762 IS
1763   full_file_name  VARCHAR2(256);
1764   l_end_pid       NUMBER := end_pid;
1765   max_pid         NUMBER;
1766   stmt            VARCHAR2(256);
1767   version         VARCHAR2(32);
1768 BEGIN
1769   full_file_name := open_log_file(log_file_name);
1770 
1771   -- Make sure the table is actually there
1772   IF (table_exists('PARTITION') = 'FALSE' ) THEN
1773     log_message('ERROR: Partition dump failed, PARTITION table not found');
1774     utl_file.fclose(part_log_file);
1775 
1776     RAISE PARAMETER_ERROR;
1777   END IF;
1778 
1779   stmt := 'SELECT MAX(PARTITION_ID) FROM PARTITION';
1780   EXECUTE IMMEDIATE stmt INTO max_pid;
1781 
1782   -- The default value for the end partition id is max(partition_id)
1786 
1783   IF (l_end_pid = -1) THEN
1784     l_end_pid := max_pid;
1785   END IF;
1787   -- Validate the starting partition id.
1788   IF ((start_pid < 0) OR (start_pid > max_pid)) THEN
1789     log_message('ERROR: Invald Start Partition ID '||start_pid||', Valid Range (0,'||max_pid||')');
1790     utl_file.fclose(part_log_file);
1791 
1792     RAISE PARAMETER_ERROR;
1793   END IF;
1794 
1795   -- Validate the ending partition id.
1796   IF ((l_end_pid < start_pid) OR (l_end_pid > max_pid) OR (l_end_pid < 0)) THEN
1797     log_message('ERROR: Invald End Partition ID '||sdo_util.number_to_char(end_pid)||', Valid Range ('||start_pid||','||max_pid||')');
1798     utl_file.fclose(part_log_file);
1799 
1800     RAISE PARAMETER_ERROR;
1801   END IF;
1802 
1803   version := get_version_info();
1804   log_message('******** Beginning partition dump');
1805   log_message('** Logfile location: ' || full_file_name, FALSE);
1806   log_message('** Routeserver data version: ' || version, FALSE);
1807   log_message('** Start partition id: ' || start_pid, FALSE);
1808   log_message('** End partition id: ' || l_end_pid, FALSE);
1809   IF (verbose) THEN
1810     log_message('** Verbose mode: TRUE', FALSE);
1811   ELSE
1812     log_message('** Verbose mode: FALSE', FALSE);
1813   END IF;
1814 
1815   log_message('', FALSE);
1816 
1817   -- Close the log file so the Java code can use it
1818   utl_file.fclose(part_log_file);
1819 
1820   elocation_dump_partition(full_file_name, start_pid, l_end_pid,
1821     verbose, is_10g(version));
1822 
1823   EXCEPTION
1824     WHEN PARAMETER_ERROR THEN
1825       raise_application_error(-20004, 'Error, partition dump failed, see log file.');
1826     WHEN OTHERS THEN
1827         IF (utl_file.is_open(part_log_file) = FALSE) THEN
1828           part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
1829         END IF;
1830         log_message(SQLERRM);
1831         utl_file.fclose(part_log_file);
1832         raise_application_error(-20004, 'Error, partition dump failed, see log file.');
1833 END dump_partitions;
1834 
1835 --
1836 -- Entry point to used to validate the partition BLOBs
1837 --
1838 PROCEDURE validate_partitions(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
1839                               start_pid IN NUMBER DEFAULT 0,
1840                               end_pid IN NUMBER DEFAULT -1,
1841                               verbose IN BOOLEAN DEFAULT FALSE)
1842 IS
1843   full_file_name  VARCHAR2(256);
1844   l_end_pid       NUMBER := end_pid;
1845   max_pid         NUMBER;
1846   stmt            VARCHAR2(256);
1847   version         VARCHAR2(32);
1848 BEGIN
1849   full_file_name := open_log_file(log_file_name);
1850 
1851   -- Make sure the table is actually there
1852   IF (table_exists('PARTITION') = 'FALSE' ) THEN
1853     log_message('ERROR: Partition validate failed, PARTITION table not found');
1854     utl_file.fclose(part_log_file);
1855 
1856     RAISE PARAMETER_ERROR;
1857   END IF;
1858 
1859   stmt := 'SELECT MAX(PARTITION_ID) FROM PARTITION';
1860   EXECUTE IMMEDIATE stmt INTO max_pid;
1861 
1862   -- The default value for the end partition id is max(partition_id)
1863   IF (l_end_pid = -1) THEN
1864     l_end_pid := max_pid;
1865   END IF;
1866 
1867   -- Validate the starting partition id.
1868   IF ((start_pid < 0) OR (start_pid > max_pid)) THEN
1869     log_message('ERROR: Invald Start Partition ID '||start_pid||
1870       ', Valid Range (0,'||max_pid||')');
1871     utl_file.fclose(part_log_file);
1872 
1873     RAISE PARAMETER_ERROR;
1874   END IF;
1875 
1876   -- Validate the ending partition id.
1877   IF ((l_end_pid < start_pid) OR (l_end_pid > max_pid) OR (l_end_pid < 0)) THEN
1878     log_message('ERROR: Invald End Partition ID '||sdo_util.number_to_char(end_pid)||
1879       ', Valid Range ('||start_pid||','||max_pid||')');
1880     utl_file.fclose(part_log_file);
1881 
1882     RAISE PARAMETER_ERROR;
1883   END IF;
1884 
1885   version := get_version_info();
1886   log_message('******** Beginning partition validation');
1887   log_message('** Logfile location: ' || full_file_name, FALSE);
1888   log_message('** Routeserver data version: ' || version, FALSE);
1889 
1890   log_message('** Start partition id: ' || start_pid, FALSE);
1891   log_message('** End partition id: ' || l_end_pid, FALSE);
1892   IF (verbose) THEN
1893     log_message('** Verbose mode: TRUE', FALSE);
1894   ELSE
1895     log_message('** Verbose mode: FALSE', FALSE);
1896   END IF;
1897   log_message('', FALSE);
1898 
1899   -- Close the log file so the Java code can use it
1900   utl_file.fclose(part_log_file);
1901 
1902   -- Adjust the Oracle JVM maximum memory size to 800M.
1903   -- This is the equivelent to specifying -Xmx800m to
1904   -- java outside the database. Memory size is specified in bytes.
1905   ElocationSetJVMHeapSize(838860800);
1906 
1907   elocation_validate_partition(full_file_name, start_pid, l_end_pid,
1908     verbose, is_10g(version));
1909 
1910   EXCEPTION
1911     WHEN PARAMETER_ERROR THEN
1912       raise_application_error(-20003, 'Error, partition validation failed, see log file.');
1913     WHEN OTHERS THEN
1914         IF (utl_file.is_open(part_log_file) = FALSE) THEN
1915           part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
1916         END IF;
1917         log_message(SQLERRM);
1918         utl_file.fclose(part_log_file);
1919         raise_application_error(-20003, 'Error, partition validation failed, see log file.');
1920 END;
1921 
1922 
1923 ---
1924 --- Entry point to partition the Routers trucking data
1925 ---
1926 PROCEDURE create_trucking_data(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
1927                                cleanup IN BOOLEAN DEFAULT TRUE)
1928 IS
1929   TYPE CURSOR_TYPE IS REF CURSOR;
1930   full_file_name  VARCHAR2(256);
1931   stmt            VARCHAR2(256);
1932 BEGIN
1933   full_file_name := open_log_file(log_file_name);
1934 
1935   log_message('******** Begin generation of trucking user data');
1936   log_message('** Logfile location: ' || full_file_name, FALSE);
1937 
1938   -- Make sure the raw truck data exists and is in Router format
1939   IF (table_exists('router_transport') = 'FALSE') THEN
1940     log_message('ERROR: ROUTER_TRANSPORT table not found');
1941     RETURN ;
1942   END IF;
1943 
1944   -- Make sure the Edge table exists
1945   IF (table_exists('EDGE') = 'FALSE') THEN
1946     log_message('ERROR: EDGE table not found');
1947     RETURN ;
1948   END IF;
1949 
1950   -- Make sure the data version table exists
1951   IF (table_exists('sdo_router_data_version') = 'FALSE') THEN
1952     log_message('ERROR: SDO_ROUTER_DATA_VERSION table not found');
1953     RETURN ;
1954   END IF;
1955 
1956   -- Cleanup the old intermediate truck data partitioning table
1957   IF (table_exists('router_partitioned_truck_data') = 'TRUE') THEN
1958     execute immediate 'DROP TABLE router_partitioned_truck_data';
1959   END IF;
1960 
1961   -- Cleanup the intermediate truck data partitioning table
1962   IF (table_exists('partitioned_router_transport') = 'TRUE') THEN
1963     execute immediate 'DROP TABLE partitioned_router_transport';
1964   END IF;
1965 
1966   -- Create truck data intermediate partitioning table
1967   stmt := 'CREATE TABLE partitioned_router_transport PARALLEL UNRECOVERABLE AS
1968     SELECT r.edge_id, partition_id, maintype, subtype, value
1969     FROM edge e, router_transport r
1970     WHERE e.edge_id=r.edge_id';
1971 
1972   EXECUTE IMMEDIATE stmt;
1973 
1974   -- Add highway edges to partition 0, the highway partition
1975   stmt := 'INSERT /*+ APPEND */ INTO partitioned_router_transport
1976     SELECT edge_id, 0, maintype, subtype, value
1977     FROM router_transport
1978     WHERE edge_id IN
1979       (SELECT edge_id FROM edge WHERE func_class=1 OR func_class=2)';
1980 
1981   EXECUTE IMMEDIATE stmt;
1982 
1983   COMMIT;
1984 
1985   -- create an index on the partitioned datas partition_id field
1986   EXECUTE IMMEDIATE 'CREATE INDEX prt_p_idx ON partitioned_router_transport(partition_id)';
1987 
1988   -- Close the log file so the Java code can use it
1989   utl_file.fclose(part_log_file);
1990 
1991   -- Adjust the Oracle JVM maximum memory size to 800M.
1992   -- This is the equivelent to specifying -Xmx800m to
1993   -- java outside the database. Memory size is specified in bytes.
1994   ElocationSetJVMHeapSize(838860800);
1995 
1996 BEGIN
1997   -- Java code to partition the trucking data
1998   elocation_trucking_data(full_file_name);
1999 EXCEPTION
2000   WHEN OTHERS THEN
2001     part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
2002     log_message(SQLERRM);
2003     utl_file.fclose(part_log_file);
2004     RAISE JAVA_ERROR;
2005 END;
2006 
2007   -- Reopen the logfile
2008   part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
2009 
2010   log_message('INFO: creating the final trucking user data table');
2011 
2012   -- Rename the new partition table and build an index on it.
2013   IF (table_exists('trucking_user_data')= 'TRUE') THEN
2014     EXECUTE IMMEDIATE 'DROP TABLE trucking_user_data';
2015   END IF;
2016 
2017   -- Rename the new partition table and build an index on it.
2018   IF (table_exists('router_trucking_data')= 'TRUE') THEN
2019     EXECUTE IMMEDIATE 'DROP TABLE router_trucking_data';
2020   END IF;
2021 
2022   EXECUTE IMMEDIATE 'RENAME new_trucking_data to router_trucking_data';
2023 
2024   log_message('INFO: create index rtud_p_idx on router_trucking_data table');
2025   EXECUTE IMMEDIATE 'CREATE INDEX rtud_p_idx on router_trucking_data(partition_id)';
2026 
2027   -- Trucking specific intermediate table cleanup
2028   IF (cleanup) THEN
2029     IF (table_exists('partitioned_router_transport') = 'TRUE') THEN
2030       EXECUTE IMMEDIATE 'DROP TABLE partitioned_router_transport';
2031     END IF;
2032     IF (table_exists('new_trucking_data') = 'TRUE') THEN
2033       EXECUTE IMMEDIATE 'DROP TABLE new_trucking_data';
2034     END IF;
2035   END IF;
2036 
2037   log_message('******** Completed generation of trucking user data ');
2038 
2039   -- Close the log file
2040   utl_file.fclose(part_log_file);
2041 EXCEPTION
2042     WHEN JAVA_ERROR THEN
2046           part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
2043         raise_application_error(-20015, 'Oracle Router trucking user data generation failed');
2044     WHEN OTHERS THEN
2045         IF (utl_file.is_open(part_log_file) = FALSE) THEN
2047         END IF;
2048         log_message(SQLERRM);
2049         utl_file.fclose(part_log_file);
2050         raise_application_error(-20015, 'Oracle Router trucking user data generation failed');
2051 END create_trucking_data;
2052 
2053 --
2054 -- Entry point to used to produce a dump of the trucking data BLOBs
2055 --
2056 PROCEDURE dump_trucking_data(
2057     log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
2058     start_pid IN NUMBER DEFAULT 0,
2059     end_pid IN NUMBER DEFAULT -1,
2060     skip_unsupported IN BOOLEAN DEFAULT TRUE)
2061 IS
2062   full_file_name  VARCHAR2(256);
2063   l_end_pid       NUMBER := end_pid;
2064   max_pid         NUMBER;
2065   stmt            VARCHAR2(256);
2066   version         VARCHAR2(32);
2067 BEGIN
2068   full_file_name := open_log_file(log_file_name);
2069 
2070   -- Make sure the table is actually there
2071   IF (table_exists('ROUTER_TRUCKING_DATA') = 'FALSE' ) THEN
2072     log_message('ERROR: Trucking data dump failed, ROUTER_TRUCKING_DATA table not found');
2073     utl_file.fclose(part_log_file);
2074 
2075     RAISE PARAMETER_ERROR;
2076   END IF;
2077 
2078   stmt := 'SELECT MAX(PARTITION_ID) FROM ROUTER_TRUCKING_DATA';
2079   EXECUTE IMMEDIATE stmt INTO max_pid;
2080 
2081   -- The default value for the end partition id is max(partition_id)
2082   IF (l_end_pid = -1) THEN
2083     l_end_pid := max_pid;
2084   END IF;
2085 
2086   -- Validate the starting partition id.
2087   IF ((start_pid < 0) OR (start_pid > max_pid)) THEN
2088     log_message('ERROR: Invald Start Partition ID '||start_pid||', Valid Range (0,'||max_pid||')');
2089     utl_file.fclose(part_log_file);
2090 
2091     RAISE PARAMETER_ERROR;
2092   END IF;
2093 
2094   -- Validate the ending partition id.
2095   IF ((l_end_pid < start_pid) OR (l_end_pid > max_pid) OR (l_end_pid < 0)) THEN
2096     log_message('ERROR: Invald End Partition ID '||sdo_util.number_to_char(end_pid)||', Valid Range ('||start_pid||','||max_pid||')');
2097     utl_file.fclose(part_log_file);
2098 
2099     RAISE PARAMETER_ERROR;
2100   END IF;
2101 
2102   version := get_version_info();
2103   log_message('******** Beginning trucking data dump');
2104   log_message('** Logfile location: ' || full_file_name, FALSE);
2105   log_message('** Routeserver data version: ' || version, FALSE);
2106   log_message('** Start partition id: ' || start_pid, FALSE);
2107   log_message('** End partition id: ' || l_end_pid, FALSE);
2108 
2109   log_message('', FALSE);
2110 
2111   -- Close the log file so the Java code can use it
2112   utl_file.fclose(part_log_file);
2113 
2114   elocation_dump_trucking_data(full_file_name, start_pid, l_end_pid, skip_unsupported);
2115 
2116   EXCEPTION
2117     WHEN PARAMETER_ERROR THEN
2118       raise_application_error(-20004, 'Error, trucking data dump failed, see log file.');
2119     WHEN OTHERS THEN
2120         IF (utl_file.is_open(part_log_file) = FALSE) THEN
2121           part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
2122         END IF;
2123         log_message(SQLERRM);
2124         utl_file.fclose(part_log_file);
2125         raise_application_error(-20004, 'Error, trucking data dump failed, see log file.');
2126 END dump_trucking_data;
2127 
2128 ---
2129 --- Entry point to partition the Routers turn restriction data
2130 ---
2131 PROCEDURE create_turn_restriction_data(log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
2132                                        cleanup IN BOOLEAN DEFAULT TRUE)
2133 IS
2134   TYPE CURSOR_TYPE IS REF CURSOR;
2135   full_file_name  VARCHAR2(256);
2136   stmt            VARCHAR2(512);
2137 BEGIN
2138   full_file_name := open_log_file(log_file_name);
2139 
2140   log_message('******** Begin generation of turn restriction user data');
2141   log_message('** Logfile location: ' || full_file_name, FALSE);
2142 
2143   -- Make sure the raw turn restriction data exists
2144   IF (table_exists('ROUTER_NAV_STRAND') = 'FALSE') THEN
2145     log_message('ERROR: ROUTER_NAV_STRAND table not found');
2146     RETURN ;
2147   END IF;
2148 
2149   -- Make sure the Edge table exists.
2150   IF (table_exists('EDGE') = 'FALSE') THEN
2151     log_message('ERROR: EDGE table not found');
2152     RETURN ;
2153   END IF;
2154 
2155   -- Make sure the data version table exists
2156   IF (table_exists('SDO_ROUTER_DATA_VERSION') = 'FALSE') THEN
2157     log_message('ERROR: SDO_ROUTER_DATA_VERSION table not found');
2158     RETURN ;
2159   END IF;
2160 
2161   -- Make sure the router_condition table exists
2162   IF (table_exists('ROUTER_CONDITION') = 'FALSE') THEN
2166 
2163     log_message('ERROR: ROUTER_CONDITION table not found');
2164     RETURN ;
2165   END IF;
2167   -- Drop the intermediate partitioned turn restriction table
2168   IF (table_exists('PARTITIONED_ROUTER_NAV_STRAND') = 'TRUE') THEN
2169     execute immediate 'DROP TABLE partitioned_router_nav_strand';
2170   END IF;
2171 
2172   -- Create the intermediate restricted driving maneuvers partitioning table
2173   stmt := 'CREATE TABLE partitioned_router_nav_strand PARALLEL UNRECOVERABLE AS
2174     SELECT nav_strand_id, seq_num, link_id, node_id, partition_id
2175     FROM  edge, router_nav_strand
2176     WHERE edge_id=link_id';
2177 
2178   EXECUTE IMMEDIATE stmt;
2179 
2180   -- Add highway edges to partition 0, the highway partition
2181   stmt := 'INSERT /*+ APPEND */ INTO partitioned_router_nav_strand
2182     SELECT nav_strand_id, seq_num, link_id, node_id, 0
2183     FROM router_nav_strand
2184     WHERE link_id IN
2185       (SELECT edge_id FROM edge WHERE func_class=1 OR func_class=2)';
2186 
2187   EXECUTE IMMEDIATE stmt;
2188 
2189   COMMIT;
2190 
2191   -- create an index on the partitioned datas partition_id field
2192   EXECUTE IMMEDIATE 'CREATE INDEX prns_pi_idx ON
2193     partitioned_router_nav_strand(partition_id) PARALLEL (DEGREE 4)';
2194   EXECUTE IMMEDIATE 'CREATE INDEX prns_li_idx ON
2195     partitioned_router_nav_strand(link_id) PARALLEL (DEGREE 4)';
2196   EXECUTE IMMEDIATE 'CREATE INDEX prns_nsi_sn_idx ON
2197     partitioned_router_nav_strand(nav_strand_id, seq_num) PARALLEL (DEGREE 4)';
2198 
2199   -- Drop the current look up table if it exists
2200   IF (table_exists('router_max_seq') = 'TRUE') THEN
2201     execute immediate 'DROP TABLE router_max_seq';
2202   END IF;
2203 
2204   -- create a lookup table to associate the max sequence number to a strand id
2205   stmt := 'CREATE TABLE router_max_seq
2206     (nav_strand_id, max_seq_num,
2207       CONSTRAINT pk_max_seq PRIMARY KEY(nav_strand_id))
2208     ORGANIZATION INDEX
2209     PARALLEL UNRECOVERABLE AS
2210       SELECT nav_strand_id, max(seq_num)
2211       FROM router_nav_strand GROUP BY nav_strand_id';
2212 
2213   EXECUTE IMMEDIATE stmt;
2214 
2215   -- Close the log file so the Java code can use it
2216   utl_file.fclose(part_log_file);
2217 
2218   -- Adjust the Oracle JVM maximum memory size to 800M.
2219   -- This is the equivelent to specifying -Xmx800m to
2220   -- java outside the database. Memory size is specified in bytes.
2221   ElocationSetJVMHeapSize(838860800);
2222 
2223 BEGIN
2224   -- Java code to partition the restricted driving maneuver data
2225   elocation_turn_restrict_data(full_file_name);
2226 EXCEPTION
2227   WHEN OTHERS THEN
2228     part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
2229     log_message(SQLERRM);
2230     utl_file.fclose(part_log_file);
2231     RAISE JAVA_ERROR;
2232 END;
2233 
2234   -- Reopen the logfile
2235   part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
2236 
2237   log_message('INFO: creating the final turn restriction user data table');
2241     EXECUTE IMMEDIATE 'DROP TABLE router_turn_restriction_data';
2238 
2239   -- Rename the new partition table and build an index on it.
2240   IF (table_exists('router_turn_restriction_data')= 'TRUE') THEN
2242   END IF;
2243 
2244   EXECUTE IMMEDIATE
2245     'RENAME new_turn_restriction_data to router_turn_restriction_data';
2246 
2247   log_message('INFO: create index rtrud_p_idx on router_turn_restriction_data table');
2248   EXECUTE IMMEDIATE 'CREATE INDEX rtrud_p_idx on router_turn_restriction_data(partition_id)';
2249 
2250   -- Turn restriction specific intermediate table cleanup
2251   IF (cleanup) THEN
2252     IF (table_exists('new_turn_restriction_data') = 'TRUE') THEN
2253       EXECUTE IMMEDIATE 'DROP TABLE new_turn_restriction_data';
2254     END IF;
2255     IF (table_exists('partitioned_router_nav_strand') = 'TRUE') THEN
2256       EXECUTE IMMEDIATE 'DROP TABLE partitioned_router_nav_strand';
2257     END IF;
2258     IF (table_exists('router_max_seq') = 'TRUE') THEN
2259       EXECUTE IMMEDIATE 'DROP TABLE router_max_seq';
2260     END IF;
2261   END IF;
2262 
2263   log_message('******** Completed generation of turn restriction user data ');
2264 
2265   -- Close the log file
2266   utl_file.fclose(part_log_file);
2267 EXCEPTION
2268     WHEN JAVA_ERROR THEN
2269         raise_application_error(-20015, 'Oracle Router turn restriction user data generation failed');
2270     WHEN OTHERS THEN
2271         IF (utl_file.is_open(part_log_file) = FALSE) THEN
2272           part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
2273         END IF;
2274         log_message(SQLERRM);
2275         utl_file.fclose(part_log_file);
2276         raise_application_error(-20015, 'Oracle Router turn restriction user data generation failed');
2277 END create_turn_restriction_data;
2278 
2279 --
2280 -- Entry point to used to produce a dump of the turn restriction BLOBs
2281 --
2282 PROCEDURE dump_turn_restriction_data(
2283     log_file_name IN VARCHAR2 := 'sdo_router_partition.log',
2284     start_pid IN NUMBER DEFAULT 0,
2285     end_pid IN NUMBER DEFAULT -1,
2286     dump_soft_restrictions IN BOOLEAN DEFAULT FALSE)
2287 IS
2288   full_file_name  VARCHAR2(256);
2289   l_end_pid       NUMBER := end_pid;
2290   max_pid         NUMBER;
2291   stmt            VARCHAR2(256);
2292   version         VARCHAR2(32);
2293 BEGIN
2294   full_file_name := open_log_file(log_file_name);
2295 
2296   -- Make sure the table is actually there
2297   IF (table_exists('ROUTER_TURN_RESTRICTION_DATA') = 'FALSE' ) THEN
2298     log_message('ERROR: Turn restriction dump failed, ROUTER_TURN_RESTRICTION_DATA table not found');
2299     utl_file.fclose(part_log_file);
2300 
2301     RAISE PARAMETER_ERROR;
2302   END IF;
2303 
2304   stmt := 'SELECT MAX(PARTITION_ID) FROM ROUTER_TURN_RESTRICTION_DATA';
2305   EXECUTE IMMEDIATE stmt INTO max_pid;
2306 
2307   -- The default value for the end partition id is max(partition_id)
2308   IF (l_end_pid = -1) THEN
2309     l_end_pid := max_pid;
2310   END IF;
2311 
2312   -- Validate the starting partition id.
2313   IF ((start_pid < 0) OR (start_pid > max_pid)) THEN
2314     log_message('ERROR: Invald Start Partition ID '||start_pid||', Valid Range (0,'||max_pid||')');
2315     utl_file.fclose(part_log_file);
2316 
2317     RAISE PARAMETER_ERROR;
2318   END IF;
2319 
2320   -- Validate the ending partition id.
2321   IF ((l_end_pid < start_pid) OR (l_end_pid > max_pid) OR (l_end_pid < 0)) THEN
2322     log_message('ERROR: Invald End Partition ID '||sdo_util.number_to_char(end_pid)||', Valid Range ('||start_pid||','||max_pid||')');
2323     utl_file.fclose(part_log_file);
2324 
2325     RAISE PARAMETER_ERROR;
2326   END IF;
2327 
2328   version := get_version_info();
2332   log_message('** Start partition id: ' || start_pid, FALSE);
2329   log_message('******** Beginning turn restriction dump');
2330   log_message('** Logfile location: ' || full_file_name, FALSE);
2331   log_message('** Routeserver data version: ' || version, FALSE);
2333   log_message('** End partition id: ' || l_end_pid, FALSE);
2334   IF (dump_soft_restrictions) THEN
2335     log_message('** Dumping soft restrictions: TRUE', FALSE);
2336   ELSE
2337     log_message('** Dumping soft restrictions: FALSE', FALSE);
2338   END IF;
2339 
2340   log_message('', FALSE);
2341 
2342   -- Close the log file so the Java code can use it
2343   utl_file.fclose(part_log_file);
2344 
2345   elocation_dump_turn_restrict(full_file_name, start_pid, l_end_pid, dump_soft_restrictions);
2346 
2347   EXCEPTION
2348     WHEN PARAMETER_ERROR THEN
2349       raise_application_error(-20004, 'Error, turn restrictions dump failed, see log file.');
2350     WHEN OTHERS THEN
2351         IF (utl_file.is_open(part_log_file) = FALSE) THEN
2352           part_log_file := utl_file.fopen ('SDO_ROUTER_LOG_DIR', log_file_name, 'A');
2353         END IF;
2354         log_message(SQLERRM);
2355         utl_file.fclose(part_log_file);
2356         raise_application_error(-20004, 'Error, turn restriction dump failed, see log file.');
2357 END dump_turn_restriction_data;
2358 
2359 --
2360 -- Entry point to cleanup tables used for debugging
2361 --
2362 PROCEDURE cleanup_router(all_tables IN BOOLEAN)
2363 IS
2364 BEGIN
2365   -- Cleanup all temporary tables.
2366   clean_tables(all_tables);
2367 END;
2368 
2369 --
2370 -- Entry point to used to get the Router data version
2371 --
2372 PROCEDURE get_version(log_file_name IN VARCHAR2 := 'sdo_router_partition.log')
2373 IS
2374   data_version    VARCHAR2(32);
2375   full_file_name  VARCHAR2(256);
2376 BEGIN
2377   full_file_name := open_log_file(log_file_name);
2378 
2379   data_version := get_version_info();
2380 
2381   dbms_output.put_line('Routeserver: data version '|| data_version);
2382   log_message('INFO: Routeserver data version: ' || data_version);
2383 
2384   utl_file.fclose(part_log_file);
2385 END;
2386 
2387 --
2388 -- main pl/sql procedure to partition a graph with coordinate information
2389 -- for recovering with
2390 --
2391 PROCEDURE recover_graph_partition(p_tab_name IN VARCHAR2,
2392                                   min_pid IN NUMBER,
2393                                   max_pid IN NUMBER,
2394                                   p_level IN NUMBER,
2395                                   max_v_no IN NUMBER,
2396                                   make_equal IN BOOLEAN)
2397 IS
2398   stmt      VARCHAR2(256);
2399   v_no      NUMBER;
2400   pid       NUMBER;
2401   p_counter NUMBER;
2402   tmp_p_tab_name VARCHAR2(32);
2403 BEGIN
2404   p_counter := max_pid+1; -- starting partition counter
2405   pid := min_pid;
2406 
2407   -- start logging
2408   log_message('INFO: starting recovery of '|| p_tab_name || ' partitioning' ||
2409     ' partition level:' || p_level || ' min(partition id):' || min_pid ||
2410       ' max(partition id)' || max_pid);
2411 
2412   FOR k IN min_pid..max_pid LOOP
2413     FOR i IN 0..p_level LOOP
2414       FOR j IN 1..power(2,i) LOOP
2415         tmp_p_tab_name := p_tab_name || '_' || pid;
2416 
2417         IF (table_exists(tmp_p_tab_name) = 'TRUE' ) THEN
2418           new_partition_proc(p_tab_name, max_v_no,pid,make_equal,p_counter);
2419 
2420           log_message('INFO:    partitioning '|| p_tab_name ||
2421             ' level: ' || j || ' partition id: ' || pid);
2422         ELSE
2423           p_counter := p_counter + 2;
2424         END IF;
2425 
2426         pid := pid +1;
2427       end loop;
2428     end loop;
2429   end loop;
2430 
2431   -- copy the result back to original table and ajust the pids
2432   adjust_final_pid(SYS.DBMS_ASSERT.SIMPLE_SQL_NAME(p_tab_name));
2433 
2434   log_message('INFO: completed recovery of '|| p_tab_name || ' partitioning');
2435 
2436 EXCEPTION
2437   WHEN OTHERS THEN
2438     log_message(SQLERRM);
2439     log_message('ERROR: exception recovering partition '|| pid ||
2440       ' of the ' || p_tab_name || ' table');
2441     raise_application_error(-20010, 'Error Recovering Graph Partitioning');
2442 END recover_graph_partition;
2443 
2444 --
2445 -- Unused procedures and functions to delete
2446 --
2447 --
2448 
2449 --
2450 -- End unused procedures and functions
2451 --
2452 
2453 END SDO_ROUTER_PARTITION;