Skip to content

Commit b4f4c39

Browse files
rYamal4AXEPOH
authored andcommitted
DBTOOLS-1666 added api
1 parent 10ea71b commit b4f4c39

File tree

54 files changed

+967
-199
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+967
-199
lines changed

README.md

Lines changed: 70 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,62 @@ A tool for easier PostgreSQL, GreenPlum, MS SQL, ClickHouse development.
99

1010
# Usage
1111

12-
API is simple. Currently static getters are available:
12+
API is simple. Currently static methods are available:
1313

1414
```java
15-
// TODO fill
15+
import org.pgcodekeeper.core.DatabaseType;
16+
import org.pgcodekeeper.core.PgCodekeeperException;
17+
import org.pgcodekeeper.core.api.DatabaseFactory;
18+
import org.pgcodekeeper.core.api.PgCodeKeeperApi;
19+
import org.pgcodekeeper.core.utils.IMonitor;
20+
import org.pgcodekeeper.core.utils.NullMonitor;
21+
import org.pgcodekeeper.core.schema.AbstractDatabase;
22+
import org.pgcodekeeper.core.settings.CoreSettings;
23+
import org.pgcodekeeper.core.settings.ISettings;
24+
25+
import java.io.IOException;
26+
27+
public class Main {
28+
public static void main(String[] args) {
29+
try {
30+
// Example 1: Compare two databases from JDBC connections and generate migration script
31+
ISettings settings = new CoreSettings(DatabaseType.PG);
32+
// settings init...
33+
AbstractDatabase oldDb = DatabaseFactory.loadFromJdbc(settings, "jdbc:postgresql://localhost/old_db");
34+
AbstractDatabase newDb = DatabaseFactory.loadFromJdbc(settings, "jdbc:postgresql://localhost/new_db");
35+
String migrationScript = PgCodeKeeperApi.diff(settings, oldDb, newDb);
36+
37+
// Example 2: Load database from project and compare with JDBC database
38+
AbstractDatabase projectDb = DatabaseFactory.loadFromProject(settings, "/path/to/project");
39+
AbstractDatabase liveDb = DatabaseFactory.loadFromJdbc(settings, "jdbc:postgresql://localhost/live_db");
40+
String script = PgCodeKeeperApi.diff(settings, projectDb, liveDb);
41+
42+
// Example 3: Compare databases with object filtering using ignore list
43+
AbstractDatabase db1 = DatabaseFactory.loadFromJdbc(settings, "jdbc:postgresql://localhost/db1");
44+
AbstractDatabase db2 = DatabaseFactory.loadFromProject(settings, "/path/to/project");
45+
String diff = PgCodeKeeperApi.diff(settings, db1, db2, "/path/to/object_ignore_list.txt");
46+
47+
// Example 4: Export database to project with filtering and progress monitoring
48+
IMonitor monitor = new NullMonitor(); // or implement custom progress monitoring
49+
AbstractDatabase db = DatabaseFactory.loadFromJdbc(settings, "jdbc:postgresql://localhost/db");
50+
PgCodeKeeperApi.export(settings, db, "/path/to/export", "/path/to/ignore_list.txt", monitor);
51+
52+
// Example 5: Update project with changes from database
53+
AbstractDatabase updatedDb = DatabaseFactory.loadFromJdbc(settings, "jdbc:postgresql://localhost/updated_db");
54+
PgCodeKeeperApi.update(settings, updatedDb, "/path/to/project");
55+
56+
// Example 6: Update project with filtering and progress monitoring
57+
PgCodeKeeperApi.update(settings, updatedDb, "/path/to/project",
58+
"/path/to/object_ignore_list.txt",
59+
"/path/to/schema_ignore_list.txt",
60+
monitor);
61+
62+
} catch (PgCodekeeperException | IOException | InterruptedException e) {
63+
System.err.println("Operation failed: " + e.getMessage());
64+
e.printStackTrace();
65+
}
66+
}
67+
}
1668
```
1769

1870
## Documentation
@@ -38,52 +90,34 @@ Build requires Java (JDK) 17+ and Apache Maven 3.9+.
3890
This project was derived from [apgdiff](https://github.com/fordfrog/apgdiff) project. At this point it is almost fully rewritten according to our needs.
3991
Primary packages of this module are:
4092

41-
- `org.pgcodekeeper.core.schema` - contains classes that describe SQL objects. Each class contains object properties, creation SQL code generator, object comparison logic and ALTER code generator. Notable classes:
42-
- `PgStatement` - all object classes must be derived from this one. Defines main API for working with objects.
43-
- `PgDatabase` - root class for every loaded database schema.
44-
- `GenericColumn` - generic reference to any object (used to be a column reference).
93+
94+
- `org.pgcodekeeper.core.api` - high-level API classes for database operations.
95+
- `org.pgcodekeeper.core.schema` - contains classes that describe SQL objects. Each class contains object properties, creation SQL code generator, object comparison logic and ALTER code generator.
4596
- `org.pgcodekeeper.core.schema.meta` - simplified SQL object classes for database structure serialization.
4697
- `org.pgcodekeeper.core.schema.meta` (resources) - serialized descriptions of PostgreSQL's system objects (built using JdbcSystemLoader).
4798
- `org.pgcodekeeper.core.loader` - database schema loaders: project, SQL file, JDBC, library.
48-
- `ProjectLoader` - loads a database from project structure. **(!)** Keep in sync with `UIProjectLoader`.
49-
- `PgDumpLoader` - loads objects into a database structure from a file. **(!)** Keep in sync with `PgUIDumpLoader`.
5099
- `org.pgcodekeeper.core.loader` (resources) - SQL queries for JdbcLoaders, used by Java code via `JdbcQueries`.
51100
- `org.pgcodekeeper.core.loader.jdbc` - classes that create schema objects based on JDBC ResultSets.
52-
- `org.pgcodekeeper.core.parsers.antlr` - generated ANTLR4 parser code, plus utility parser classes. Notable classes:
53-
- `AntlrParser` - factory methods for parser creation and parallel parser operations.
54-
- `QNameParser` - utility class for parsing and splitting qualified names.
55-
- `ScriptParser` - simple parser that splits statements for later execution.
56-
- `Custom(T)SQLParserListener` - main entry point for parsing SQLs into schema objects.
57-
- `org.pgcodekeeper.core.parsers.antlr.statements` - processor classes that create schema objects based on parser-read data. Notable classes:
58-
- `ParserAbstract` - common base class for all processors.
101+
- `org.pgcodekeeper.core.parsers.antlr` - generated ANTLR4 parser code, plus utility parser classes.
102+
- `org.pgcodekeeper.core.parsers.antlr.statements` - processor classes that create schema objects based on parser-read data.
59103
- `org.pgcodekeeper.core.parsers.antlr.expr` - expression analysis classes: find dependencies in expressions and infer expression types for overloaded function call resolution.
60104
- `org.pgcodekeeper.core.parsers.antlr.expr.launcher` - support for parallel expression analysis.
61105
- `org.pgcodekeeper.core.formatter` - SQL and pl/pgsql code formatters.
62-
- `org.pgcodekeeper.core.model.difftree` - classes representing and creating an object diff tree. Notable classes:
63-
- `DbObjType` - enum of all SQL object types supported by the program.
64-
- `TreeElement` - a node in an object diff tree.
65-
- `DiffTree` - object diff tree factory.
66-
- `TreeFlattener` - filters and flattens the tree into a list of nodes according to requested parameters.
67-
- `org.pgcodekeeper.core.model.graph` - object dependency graph classes, built using JGraphT library. Notable classes:
68-
- `DepcyGraph` - the dependency graph and its builder.
69-
- `DepcyResolver` - main script building algorithm. Creates a list of `StatementAction`s according to requested object changes and object dependencies.
70-
- `DepcyTreeExtender` - extends an object diff tree according to object dependencies.
71-
- `DepcyWriter` - prints a dependency tree for an object.
72-
- `org.pgcodekeeper.core.model.exporter` - classes that save and update the project files on disk. Notable classes:
73-
- `ModelExporter`, `MsModelExporter`, `AbstractModelExporter` - exporters for PG and MS projects and their common abstract part.
74-
- `OverridesModelExporter` - exports project structure containing only permission overrides.
106+
- `org.pgcodekeeper.core.model.difftree` - classes representing and creating an object diff tree.
107+
- `org.pgcodekeeper.core.model.graph` - object dependency graph classes, built using JGraphT library.
108+
- `org.pgcodekeeper.core.model.exporter` - classes that save and update the project files on disk.
75109
- `org.pgcodekeeper.core.sql` - a categorized list of all PostgreSQL keywords. Generated from PostgreSQL source.
76110
- `org.pgcodekeeper.core.ignoreparser` - builder for `IgnoreList`s.
77-
- `org.pgcodekeeper.core` - main package containing general stuff: e.g. string constants, utils and general-purpose classes. Notable classes:
78-
- `PgDiff` - entry point for database schema diff operations.
111+
- `org.pgcodekeeper.core` - main package containing general stuff: e.g. string constants, utils and general-purpose classes.
79112
- `src.main.antlr4.org.pgcodekeeper.core.parsers.antlr.generated` - sources for ANTLR4 parsers. We maintain parsers for PostgreSQL, pl/pgsql, T-SQL, and also our custom Ignore Lists and PostgreSQL ACLs syntax.
80113
These need to be built using your preferred ANTLR4 builder into `ru.taximaxim.codekeeper.core.parsers.antlr` package.
81114

82-
Majority of tests are here.
115+
Majority of tests are here.
83116

84-
- `org.pgcodekeeper.core` - `PgDiffTest`, `MsDiffTest` - these test cases load old and new database schemas, generate a migration script and compare it to the expected diff file.
117+
- `org.pgcodekeeper.core.api` - integration tests for the high-level API operations (diff, export, update) using real test databases and projects.
118+
- `org.pgcodekeeper.core` - these test cases load old and new database schemas, generate a migration script and compare it to the expected diff file.
85119
- `org.pgcodekeeper.core.depcies` - tests here work similarly to simple diff tests above, except the concept of "user selection" is added. Migration script is built only with objects in `usr` files as starting points, other objects must be added to the script via dependency mechanism.
86-
- `org.pgcodekeeper.core.loader` - `PgAntlrLoaderTest`, `MsAntlrLoaderTest` - these tests load simple schemas from files and compare loaded PgDatabase objects with predefined ones.
120+
- `org.pgcodekeeper.core.loader` - these tests load simple schemas from files and compare loaded AbstractDatabase objects with predefined ones.
87121
- `org.pgcodekeeper.core.parsers` - these tests simply parse test pieces of code (taken from PostgreSQL source) to verify parser validity.
88122
- `org.pgcodekeeper.core.parsers.antlr.expr` - these tests verify expression analysis and type inference mechanism.
89123
- `org.pgcodekeeper.core.model.exporter` - these test update exported project directories and verify which files have actually been changed.
@@ -92,11 +126,11 @@ Majority of tests are here.
92126

93127
General program lifecycle goes as follows:
94128
1. `ISettings` object is filled with operation parameters.
95-
2. `PgDatabase`s are loaded from requested sources, including their libraries and privilege overrides. Ignored schemas are skipped at this step.
129+
2. `AbstractDatabase`s are loaded from requested sources, including their libraries and privilege overrides. Ignored schemas are skipped at this step.
96130
1. During the load dependencies of each object are found and recorded. Expressions are also analyzed to extract their dependencies including overloaded function calls.
97131
2. All parser and expression analysis operations are run in parallel using `AntlrParser.ANTLR_POOL` thread pool to speed up the process. Parallel operations are serialized by calling `finishLoaders` at the end of each loading process.
98-
3. The diff tree (represented by root `TreeElement`) is created by comparing two `PgDatabase`s. In GUI this is then shown to the user to assess the situation and choose objects to work with.
132+
3. The diff tree (represented by root `TreeElement`) is created by comparing two `AbstractDatabase`s.
99133
4. The diff tree, now containing "user selection", is used to selectively update project files on disk, or to generate a migration script.
100134
5. In latter case, each "selected" TreeElement is passed to `DepcyResolver` to generate script actions fulfilling the requested change, including actions on dependent objects. To do this, JGraphT object dependency graphs are built using dependency information found at the loading stage.
101135
6. Generated actions are now converted into SQL code with some last-moment post-processing and filtering.
102-
7. Generated SQL script is written to a file/stdout or shown in the GUI for user to review and run on their database.
136+
7. Generated SQL script is returned as a String for user to review and run on their database.

src/main/java/org/pgcodekeeper/core/PgDiffUtils.java

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
*******************************************************************************/
2020
package org.pgcodekeeper.core;
2121

22-
import org.eclipse.core.runtime.IProgressMonitor;
2322
import org.pgcodekeeper.core.sql.Keyword;
2423
import org.pgcodekeeper.core.sql.Keyword.KeywordCategory;
24+
import org.pgcodekeeper.core.utils.IMonitor;
2525
import org.slf4j.Logger;
2626
import org.slf4j.LoggerFactory;
2727

@@ -132,7 +132,6 @@ public static boolean isValidIdChar(char c, boolean allowCaps, boolean allowDigi
132132
* quoted.
133133
*
134134
* @param name name
135-
*
136135
* @return quoted string if needed, otherwise not quoted string
137136
*/
138137
public static String getQuotedName(final String name) {
@@ -163,6 +162,7 @@ public static String quoteString(String s) {
163162
* Quotes string using dollar-quoting ($$) syntax.
164163
* <p>
165164
* Function equivalent to appendStringLiteralDQ in pgdump's dumputils.c
165+
*
166166
* @param contents the string to quote
167167
* @return dollar-quoted string
168168
*/
@@ -222,7 +222,7 @@ public static byte[] getHash(String s, String instance) throws NoSuchAlgorithmEx
222222
* @param instance the hash algorithm to use
223223
* @return hexadecimal hash string
224224
*/
225-
public static String hash (String s, String instance) {
225+
public static String hash(String s, String instance) {
226226
try {
227227
byte[] hash = getHash(s, instance);
228228
StringBuilder sb = new StringBuilder(2 * hash.length);
@@ -233,12 +233,13 @@ public static String hash (String s, String instance) {
233233
return sb.toString();
234234
} catch (NoSuchAlgorithmException e) {
235235
LOG.error(e.getLocalizedMessage(), e);
236-
return instance +"_ERROR_" + RANDOM.nextInt();
236+
return instance + "_ERROR_" + RANDOM.nextInt();
237237
}
238238
}
239239

240240
/**
241241
* Returns MD5 hash of string as hexadecimal.
242+
*
242243
* @param s the string to hash
243244
* @return lowercase hex MD5 for UTF-8 representation of given string
244245
*/
@@ -248,6 +249,7 @@ public static String md5(String s) {
248249

249250
/**
250251
* Returns SHA-256 hash of string as hexadecimal.
252+
*
251253
* @param s the string to hash
252254
* @return lowercase hex SHA-256 for UTF-8 representation of given string
253255
*/
@@ -297,7 +299,7 @@ public static String getErrorSubstr(String s, int pos, int len) {
297299
* @param monitor the progress monitor to check
298300
* @throws InterruptedException if monitor is cancelled
299301
*/
300-
public static void checkCancelled(IProgressMonitor monitor)
302+
public static void checkCancelled(IMonitor monitor)
301303
throws InterruptedException {
302304
if (monitor != null && monitor.isCanceled()) {
303305
throw new InterruptedException();
@@ -311,7 +313,7 @@ public static void checkCancelled(IProgressMonitor monitor)
311313
* <code>List.containsAll()</code> O(N^2) call here. In general, duplicate elimination is an undesired side-effect
312314
* of comparison using {@link Set}s, so this solution is overall better and only *slightly* slower.<br>
313315
* <br>
314-
*
316+
* <p>
315317
* Performance: best case O(1), worst case O(N) + new {@link HashMap} (in case N1 == N2), assuming size() takes
316318
* constant time.
317319
*
@@ -331,7 +333,7 @@ public static boolean setlikeEquals(Collection<?> c1, Collection<?> c2) {
331333
// mimic HashSet(Collection) constructor
332334
final float loadFactor = 0.75f;
333335
final Map<Object, Integer> map =
334-
new HashMap<>(Math.max((int) (s1/loadFactor) + 1, 16), loadFactor);
336+
new HashMap<>(Math.max((int) (s1 / loadFactor) + 1, 16), loadFactor);
335337
for (Object el1 : c1) {
336338
map.compute(el1, (k, i) -> i == null ? 1 : (i + 1));
337339
}
@@ -414,17 +416,17 @@ public static void appendSqlWrappedInDo(StringBuilder sbResult, StringBuilder sb
414416
String body = sbSQL.toString().replace("\n", "\n\t");
415417

416418
sbResult
417-
.append("DO $$")
418-
.append("\nBEGIN")
419-
.append("\n\t").append(body)
420-
.append("\nEXCEPTION WHEN OTHERS THEN")
421-
.append("\n\tIF (SQLSTATE = ").append(expectedErrCode).append(") THEN")
422-
.append("\n\t\tRAISE NOTICE '%, skip', SQLERRM;")
423-
.append("\n\tELSE")
424-
.append("\n\t\tRAISE;")
425-
.append("\n\tEND IF;")
426-
.append("\nEND; $$")
427-
.append("\nLANGUAGE 'plpgsql'");
419+
.append("DO $$")
420+
.append("\nBEGIN")
421+
.append("\n\t").append(body)
422+
.append("\nEXCEPTION WHEN OTHERS THEN")
423+
.append("\n\tIF (SQLSTATE = ").append(expectedErrCode).append(") THEN")
424+
.append("\n\t\tRAISE NOTICE '%, skip', SQLERRM;")
425+
.append("\n\tELSE")
426+
.append("\n\t\tRAISE;")
427+
.append("\n\tEND IF;")
428+
.append("\nEND; $$")
429+
.append("\nLANGUAGE 'plpgsql'");
428430
}
429431

430432
private PgDiffUtils() {

0 commit comments

Comments
 (0)