Project

General

Profile

missinng-udfs.diff

Igor Skornyakov, 07/12/2022 12:00 PM

Download (17.8 KB)

View differences:

src/com/goldencode/p2j/persist/Persistence.java 2022-07-12 15:57:49 +0000
567 567
**     IAS 20220324          Re-worked LockTableUpdater.
568 568
**     OM  20220516          Added collation support for _meta databases (use the H2 dialect).
569 569
**     IAS 20220608          Provide additional information in reportUDFVersion().
570
**     IAS 20220712          Create missing UDFs
570 571
*/
571 572

  
572 573
/*
......
632 633
import java.util.logging.*;
633 634
import com.goldencode.cache.*;
634 635
import com.goldencode.p2j.main.*;
636
import com.goldencode.p2j.persist.deploy.*;
635 637
import com.goldencode.p2j.persist.dialect.*;
636 638
import com.goldencode.p2j.persist.event.*;
637 639
import com.goldencode.p2j.persist.hql.*;
......
1021 1023
      String searchPath = "unknown";
1022 1024
      
1023 1025
      try (Session session = new Session(database);
1024
           Statement st = session.getConnection().createStatement())
1026
           Connection conn = session.getConnection())
1025 1027
      {
1026 1028
         String sqlFunc = HQLPreprocessor.getRegisteredFunction(database, "getFWDVersion", null);
1027 1029
         if (sqlFunc == null)
1028 1030
         {
1029 1031
            sqlFunc = "getFWDVersion"; // the function is not overloaded for all dialects
1030 1032
         }
1031
         useSQLUdfs = (dialect instanceof P2JPostgreSQLDialect) &&
1033
         useSQLUdfs = dialect.isNativeUDFsSupported() &&
1032 1034
                  !DatabaseManager.getConfiguration(database).isUseJavaUDFs();
1033
         if (useSQLUdfs && !sqlFunc.startsWith("udf."))
1035
         if (useSQLUdfs)
1034 1036
         {
1035
            sqlFunc = "udf." + sqlFunc;
1037
            ScriptRunner.createMissingUdfs(database);
1038
            if (!sqlFunc.startsWith("udf."))
1039
            {
1040
               sqlFunc = "udf." + sqlFunc;
1041
            }
1036 1042
         }
1037
         try
1043
         try(Statement st = conn.createStatement(); 
1044
            ResultSet rs = st.executeQuery("SELECT " + sqlFunc + "()"))
1038 1045
         {
1039
            ResultSet rs = st.executeQuery("SELECT " + sqlFunc + "()");
1040 1046
            if (rs.next())
1041 1047
            {
1042 1048
               String val = rs.getString(1);
......
1054 1060
         }
1055 1061
         if (dialect instanceof P2JPostgreSQLDialect)
1056 1062
         {
1057
            try
1063
            try(Statement st = conn.createStatement(); 
1064
                ResultSet rs = st.executeQuery(
1065
                              "SELECT setting FROM pg_settings WHERE name='search_path'"))
1058 1066
            {
1059
               ResultSet rs = st.executeQuery(
1060
                        "SELECT setting FROM pg_settings WHERE name='search_path'");
1061 1067
               if (rs.next())
1062 1068
               {
1063 1069
                  String val = rs.getString(1);
src/com/goldencode/p2j/persist/deploy/ScriptRunner.java 2022-07-12 15:58:16 +0000
10 10
**     IAS 20220608 Add option for setting database search_path.
11 11
**     IAS 20220610 Create 'scale' UDF if PostgreSQL major version is < 10.
12 12
**     OM  20220614 Dropped unused import. Formatting javadoc and typo fixes.
13
**     IAS 20220712 Create missing UDFs
13 14
*/
14 15

  
15 16
/*
......
74 75
import java.util.regex.*;
75 76
import java.util.stream.*;
76 77
import com.goldencode.p2j.cfg.*;
78
import com.goldencode.p2j.persist.*;
77 79
import com.goldencode.p2j.persist.deploy.ScriptSplitter.*;
78 80
import com.goldencode.p2j.persist.dialect.*;
81
import com.goldencode.p2j.persist.orm.*;
79 82
import com.goldencode.p2j.util.*;
80 83

  
81 84
/** Run SQL script against PostgreSQL database */
......
112 115
         "$function$"
113 116
      )
114 117
   );
115
   
118

  
119
   private static final List<String> COUNT = Collections.unmodifiableList(
120
            Arrays.asList(
121
               "select count(*)",
122
               "from pg_proc p",
123
               "left join pg_namespace n on p.pronamespace = n.oid",
124
               "left join pg_language l on p.prolang = l.oid",
125
               "left join pg_type t on t.oid = p.prorettype",
126
               "where n.nspname = ?",
127
               "  and l.lanname in ('sql', 'plpgsql')",
128
               "  and t.typname != 'trigger'",
129
               "  and p.proname = ?"
130
            )
131
         );
132

  
116 133
   /**
117 134
    * Runner main method.
118 135
    *
......
136 153
      {
137 154
         throw new IllegalArgumentException("Unknown JDBC database type: [" + dbtype + "]");
138 155
      }
139
      ScriptSplitter splitter = dialect.scripSplitter();
140
      List<String> scriptNames;
156
      List<String> scripts;
141 157
      boolean setSearchPath = false; 
142 158
      boolean createScale = false;
143 159
      switch (args[3])
......
145 161
         case "udf.install.search_path":
146 162
            setSearchPath = true;
147 163
         case "udf.install":
148
            String[] scripts;
149 164
            if (args.length > 4)
150 165
            {
151
               scripts = args[4].split("\\s+");
166
               scripts = Arrays.asList(args[4].split("\\s+"));
152 167
            }
153 168
            else
154 169
            {
155
               scripts = new String[] {"udfs.sql", "words-udfs-sql.sql"}; 
170
               scripts = Arrays.asList("udfs.sql", "words-udfs-sql.sql"); 
156 171
            }
157
            scriptNames = Arrays.stream(scripts).map(s -> "/udf/" + dbtype + "/" + s).
158
                     collect(Collectors.toList());
159 172
            break;
160 173
         default:
161 174
            throw new IllegalArgumentException("Unknown command: [" + args[3] + "]");
......
164 177
      
165 178
      try(Connection conn = DriverManager.getConnection(args[0], args[1], args[2]))
166 179
      {
167
         boolean isPostgreSQL =  "postgresql".equalsIgnoreCase(
168
                  conn.getMetaData().getDatabaseProductName());
169
         if (isPostgreSQL && getPgVersion(conn) < 1000)
170
         {
171
            createScaleUDF(conn);
172
         }
173
         
174
         for (String scriptName: scriptNames)
175
         {
176
            InputStream is = ScriptRunner.class.getResourceAsStream(scriptName);
177
            if (is == null)
178
            {
179
               throw new IllegalStateException("Cannot open script [" + scriptName + "]" );
180
            }
181
            try(BufferedReader br = new BufferedReader(new InputStreamReader(is)))
182
            {
183
               LOG.log(Level.INFO, "Running script: [{}]", scriptName);
184
               runScript(conn, br, splitter);
185
            }
186
         }
187
         if (setSearchPath && isPostgreSQL)
188
         {
189
            String dbname = null;
190
            try(Statement stmt = conn.createStatement())
191
            {
192
               ResultSet rs = stmt.executeQuery("SELECT current_database()");
193
               if( rs.next())
194
               {
195
                  dbname = rs.getString(1);
196
               }
197
            }
198
            if (dbname != null)
199
            {
200
               LOG.log(Level.INFO, "Setting search_path for [{}]", dbname);
201
               execute(conn, "ALTER DATABASE " + dbname + " SET search_path TO public,udf");
202
            }
203
            else
204
            {
205
               LOG.log(Level.WARNING, "Failed to retrieve the database name");
206
            }
180
         apply(dialect, scripts, setSearchPath, conn);
181
      }
182
   }
183
   
184
   /**
185
    * Check for missing UDFs and create them.
186
    * 
187
    * @param db
188
    *       Database.
189
    * @throws SQLException
190
    *         on SQL error.
191
    * @throws PersistenceException
192
    *         on script reading error.
193
    */
194
   public static void createMissingUdfs(Database db) 
195
   throws SQLException, 
196
          PersistenceException
197
   {
198
      String dbname = db.getName();
199
      Dialect dialect = DatabaseManager.getDialect(db);
200
      Settings orm = DatabaseManager.getConfiguration(db).getOrmSettings();
201
      String url = orm.getString(Settings.URL, null);
202
      String adm = orm.getString(Settings.ADMIN, null);
203
      String pwd = orm.getString(Settings.ADMIN_PASSWORD, null);
204
      if (adm == null || pwd == null)
205
      {
206
         LOG.info("No admin credentials provided, skipping missing UDFs analysis");
207
         return;
208
      }
209
      List<String> scripts = new ArrayList<>();
210
      try(Connection conn = DriverManager.getConnection(url, adm, pwd))
211
      {
212
         if (countUDFs(conn, "udf", "getfwdversion") == 0)
213
         {
214
            LOG.info(String.format("%s UDF not found in %s; %s SQL script will be applied", 
215
                     "'udf.getfwdversion'", dbname, "'udfs.sql'"));
216
            scripts.add("udfs.sql");
217
         }
218
         if (countUDFs(conn, "public", "words") < 2)
219
         {
220
            LOG.info(String.format("Less than 2 %s UDFs found in %s; %s SQL script will be applied", 
221
                     "'public.words'", dbname, "'words-udfs-sql.sql'"));
222
            scripts.add("words-udfs-sql.sql");
223
         }
224
         try
225
         {
226
            apply(dialect, scripts, false, conn);
227
         }
228
         catch (IOException e)
229
         {
230
            throw new PersistenceException(e);
231
         }
232
      }
233
   }
234

  
235
   /**
236
    * Apply SQL scripts.
237
    * @param dialect
238
    *        Database dialect.
239
    * @param scripts
240
    *        Scripts to be applied.
241
    * @param setSearchPath
242
    *        Flag indicating that serach path should be set.  
243
    * @param conn
244
    *        Database connection.
245
    * @throws SQLException
246
    *         on SQL error.
247
    * @throws IOException
248
    *         on script reading error.
249
    */
250
   private static void apply(Dialect dialect, List<String> scripts,
251
            boolean setSearchPath, Connection conn) throws SQLException, IOException
252
   {
253
      ScriptSplitter splitter = dialect.scripSplitter();
254
      List<String> scriptNames = scripts.stream().
255
               map(s -> "/udf/" + dialect.getJdbcType() + "/" + s).
256
               collect(Collectors.toList());
257
      boolean isPostgreSQL =  "postgresql".equalsIgnoreCase(
258
               conn.getMetaData().getDatabaseProductName());
259
      if (isPostgreSQL && getPgVersion(conn) < 1000)
260
      {
261
         createScaleUDF(conn);
262
      }
263
      
264
      for (String scriptName: scriptNames)
265
      {
266
         InputStream is = ScriptRunner.class.getResourceAsStream(scriptName);
267
         if (is == null)
268
         {
269
            throw new IllegalStateException("Cannot open script [" + scriptName + "]" );
270
         }
271
         try(BufferedReader br = new BufferedReader(new InputStreamReader(is)))
272
         {
273
            LOG.log(Level.INFO, String.format("Running script: [%s]", scriptName));
274
            runScript(conn, br, splitter);
275
         }
276
      }
277
      if (setSearchPath && isPostgreSQL)
278
      {
279
         String dbname = null;
280
         try(Statement stmt = conn.createStatement();
281
             ResultSet rs = stmt.executeQuery("SELECT current_database()"))
282
         {
283
            if( rs.next())
284
            {
285
               dbname = rs.getString(1);
286
            }
287
         }
288
         if (dbname != null)
289
         {
290
            LOG.log(Level.INFO, String.format("Setting search_path for [%s]", dbname));
291
            execute(conn, "ALTER DATABASE " + dbname + " SET search_path TO public,udf");
292
         }
293
         else
294
         {
295
            LOG.log(Level.WARNING, "Failed to retrieve the database name");
207 296
         }
208 297
      }
209 298
   }
......
351 440
    *          The database connection.
352 441
    *
353 442
    * @throws  SQLException
354
    *          Oon error.
443
    *          On error.
355 444
    */
356 445
   private static void createScaleUDF(Connection conn)
357 446
   throws SQLException
......
361 450
      String sql = SCALE.stream().collect(Collectors.joining(eoln));
362 451
      execute(conn, sql);
363 452
   }
453

  
454
   /**
455
    * Count number of UDFs with a given name.
456
    * @param   conn
457
    *          The database connection.
458
    * @param   schema
459
    *          schema name.
460
    * @param   name
461
    *          UDF name.
462
    * @return  number of UDFs with a given name.         
463
    * @throws  SQLException
464
    *          On error.
465
    */
466
   private static int countUDFs(Connection conn, String schema, String name)
467
   throws SQLException
468
   {
469
      String eoln = EnvironmentOps.OS_WIN.equalsIgnoreCase(Configuration.getParameter("opsys"))
470
            ? "\r\n" /* express to WINDOWS */ : "\n" /* default to Linux */;  
471
      String sql = COUNT.stream().collect(Collectors.joining(eoln));
472
      try (PreparedStatement pstmt = conn.prepareStatement(sql))
473
      {
474
         int n = 0;
475
         pstmt.setString(1, schema);
476
         pstmt.setString(2, name);
477
         ResultSet rs = pstmt.executeQuery();
478
         if (rs.next())
479
         {
480
            n = rs.getInt(1);
481
         }
482
         return n;
483
      }
484
   }
364 485
}
src/com/goldencode/p2j/persist/dialect/Dialect.java 2022-07-12 15:54:37 +0000
78 78
**     OM  20210412          Replace Hibernate-specific casts with dialect-specific casts
79 79
**     IAS 20211223          Special processing of errors raised by UDFs. 
80 80
**     IAS 20220112          Resolve dialect by JDBC database type. 
81
**     IAS 20220602          Added  isNativeUDFsSupported() method
81
**     IAS 20220602          Added isNativeUDFsSupported() method.
82
**     IAS 20220712          Added getJdbcType() method.
82 83
*/
83 84

  
84 85
/*
......
315 316
    */
316 317
   public abstract ScriptSplitter scripSplitter();
317 318
   
319
   /** 
320
    * Get the dialect JDBC type.
321
    * @return dialect JDBC type.
322
    */
323
   public abstract String getJdbcType();
324

  
318 325
   /**
319 326
    * Create a {@link Blob} instance from the specified bytes.
320 327
    * 
src/com/goldencode/p2j/persist/dialect/P2JH2Dialect.java 2022-07-12 15:55:05 +0000
94 94
**     OM  20210908          Used SchemaDictionary.TEMP_TABLE_DB instead of hardcoded constant.
95 95
**     IAS 20211223          Special processing of errors raised by UDFs. 
96 96
**     IAS 20220112          Resolve dialect by JDBC database type. 
97
**     IAS 20220602          Added  isNativeUDFsSupported() method
97
**     IAS 20220602          Added isNativeUDFsSupported() method.
98
**     IAS 20220712          Added getJdbcType() method.
98 99
*/
99 100

  
100 101
/*
......
1409 1410
   }
1410 1411
   
1411 1412

  
1413
   /** 
1414
    * Get the dialect JDBC type.
1415
    * @return dialect JDBC type.
1416
    */
1417
   public String getJdbcType()
1418
   {
1419
      return "h2";
1420
   }
1421
   
1412 1422
   /**
1413 1423
    * Create a {@link Blob} instance from the specified bytes.
1414 1424
    * 
src/com/goldencode/p2j/persist/dialect/P2JPostgreSQLDialect.java 2022-07-12 15:55:23 +0000
114 114
**     IAS 20220112          Resolve dialect by JDBC database type.
115 115
**     TJD 20220331          Removed reference to IOUtils.toInputStream
116 116
**     IAS 20220601          Do not emit 'words' UDFs definitions
117
**     IAS 20220602          Added  isNativeUDFsSupported() method
117
**     IAS 20220602          Added isNativeUDFsSupported() method.
118
**     IAS 20220712          Added getJdbcType() method.
118 119
*/
119 120

  
120 121
/*
......
1776 1777
      return new PgScriptSplitter();
1777 1778
   }
1778 1779

  
1780
   /** 
1781
    * Get the dialect JDBC type.
1782
    * @return dialect JDBC type.
1783
    */
1784
   public String getJdbcType()
1785
   {
1786
      return "postgresql";
1787
   }
1788

  
1779 1789
   /**
1780 1790
    * Create a {@link Blob} instance from the specified bytes.
1781 1791
    * 
src/com/goldencode/p2j/persist/dialect/P2JSQLServer2008Dialect.java 2022-07-12 15:55:49 +0000
34 34
**     CA  20210305 The object and handle fields have a Long representation at the SQL table field.
35 35
**     IAS 20211223 Special processing of errors raised by UDFs (placeholder). 
36 36
**     IAS 20220112 Resolve dialect by JDBC database type. 
37
**     IAS 20220602          Added  isNativeUDFsSupported() method
37
**     IAS 20220602 Added isNativeUDFsSupported() method.
38
**     IAS 20220712 Added getJdbcType() method.
38 39
*/
39 40

  
40 41
/*
......
1234 1235
      throw new UnsupportedOperationException();
1235 1236
   }
1236 1237

  
1238
   /** 
1239
    * Get the dialect JDBC type.
1240
    * @return dialect JDBC type.
1241
    */
1242
   public String getJdbcType()
1243
   {
1244
      return "sqlserver";
1245
   }
1246

  
1237 1247
   /**
1238 1248
    * Create a {@link Blob} instance from the specified bytes.
1239 1249
    * 
src/com/goldencode/p2j/persist/orm/Settings.java 2022-07-12 15:53:13 +0000
2 2
** Module   : Settings.java
3 3
** Abstract : Configuration information required for the ORM framework
4 4
**
5
** Copyright (c) 2019-2020, Golden Code Development Corporation.
5
** Copyright (c) 2019-2022, Golden Code Development Corporation.
6 6
**
7 7
** -#- -I- --Date-- ---------------------------------Description---------------------------------
8 8
** 001 ECF 20191001 Created initial version. ORM layer configuration information.
......
10 10
** 003 AIL 20200918 Made Settings use the dynamically computed URL for per-session databases.
11 11
** 004 AIL 20201016 Increased searching interval for getAllForCategory.
12 12
** 005 IAS 20201219 Added 'CPSTREAM' import task argument
13
** 006 IAS 20220712 Added admin login/pwd values.
13 14
*/
14 15

  
15 16
/*
......
95 96
   /** Connection password key */
96 97
   public static final String PASSWORD = "connection.password";
97 98
   
99
   /** Connection uadmin name key */
100
   public static final String ADMIN = "connection.admin_username";
101
   
102
   /** Connection password key */
103
   public static final String ADMIN_PASSWORD = "connection.admin_password";
104

  
98 105
   /** table dump file codepage, optional */
99 106
   public static final String CPSTREAM = "dump.cpstream";
100 107