A few changes:
[cfb.git] / prod / net / jaekl / cfb / db / Schema.java
1 package net.jaekl.cfb.db;
2
3 // Copyright (C) 2015 Christian Jaekl
4
5 import java.sql.Connection;
6 import java.sql.DatabaseMetaData;
7 import java.sql.ResultSet;
8 import java.sql.SQLException;
9 import java.util.ArrayList;
10 import java.util.HashSet;
11 import java.util.Locale;
12
13 import net.jaekl.cfb.db.driver.DbDriver;
14
15 public class Schema {
16         String m_name;
17         DbDriver m_driver;
18         ArrayList<Table> m_tables;
19         ArrayList<Sequence> m_sequences;
20         
21         public Schema(String name, DbDriver driver) {
22                 m_name = name;
23                 m_driver = driver;
24                 m_tables = new ArrayList<Table>();
25                 m_sequences = new ArrayList<Sequence>();
26         }
27         
28         public boolean ensureDbInitialized(Connection con) throws SQLException {
29                 assert(null != con);
30                 
31                 if (allTablesPresent(con)) {
32                         return true;
33                 }
34                 
35                 if (!createAllTables(con)) {
36                         return false;
37                 }
38                 
39                 if (!createAllSequences(con)) { 
40                         return false;
41                 }
42                 
43                 if (!postCreationInit(con)) {
44                         return false;
45                 }
46                 
47                 return true;
48         }
49         
50         public void purge(Connection con) throws SQLException {
51                 dropAllTables(con);
52                 dropAllSequences(con);
53         }
54         
55         boolean postCreationInit(Connection con) throws SQLException {
56                 // no-op
57                 // Override this in a derived class if you need to initialize something 
58                 // after the tables and sequences are created.
59                 return true;
60         }
61         
62         boolean allTablesPresent(Connection con) throws SQLException 
63         {
64                 assert(null != con);
65                 
66                 DatabaseMetaData dbmd = con.getMetaData();
67                 HashSet<String> extantTables = new HashSet<String>();
68                 
69                 try (ResultSet rs = dbmd.getTables(null, null, null, new String[]{"TABLE"})) {
70                         while (rs.next()) {
71                                 extantTables.add(rs.getString(3).toUpperCase(Locale.CANADA));
72                         }
73                 }
74                 
75                 for (Table table : m_tables) {
76                         String name = table.getName().toUpperCase(Locale.CANADA);
77                         if ( ! extantTables.contains(name) ) {
78                                 // One or more tables missing
79                                 return false;
80                         }
81                 }
82                 
83                 // We could be more thorough here, and check that the expected columns are in place.
84                 // Also, eventually, some sort of DB schema versioning will be needed.
85                 
86                 return true;            
87         }
88         
89         boolean createAllTables(Connection con) throws SQLException {
90                 for (Table table : m_tables) {
91                         if (!m_driver.createTable(con, table)) {
92                                 return false;
93                         }
94                 }
95                 return true;
96         }
97         
98         void dropAllTables(Connection con) {
99                 for (Table table : m_tables) {
100                         try {
101                                 m_driver.dropTable(con, table);
102                         }
103                         catch (SQLException e) {
104                                 e.printStackTrace();
105                         }
106                 }
107         }
108         
109         boolean createAllSequences(Connection con) throws SQLException {
110                 for (Sequence seq : m_sequences) {
111                         if (!m_driver.createSequence(con, seq)) {
112                                 return false;
113                         }
114                 }
115                 return true;
116         }
117         
118         void dropAllSequences(Connection con) {
119                 for (Sequence seq : m_sequences) {
120                         try {
121                                 m_driver.dropSequence(con, seq);
122                         }
123                         catch (SQLException e) {
124                                 e.printStackTrace();
125                         }
126                 }
127         }
128         
129         void addTable(Table table) {
130                 m_tables.add(table);
131         }
132         
133         // Add a list of tables.
134         // Define each table in the list as follows:
135         // {
136         //   { table_name },
137         //   { column_name, type, width (-1 for default), null/not_null }
138         // }
139         void addTables(Table[] tables) 
140         {
141                 for (Table table : tables) {
142                         addTable(table);
143                 }
144         }
145         
146         void addSequence(Sequence seq) {
147                 m_sequences.add(seq);
148         }
149         
150         void addSequences(Sequence[] sequences) {
151                 for (Sequence sequence : sequences) {
152                         addSequence(sequence);
153                 }
154         }
155 }