or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

changelog.mdcommand-framework.mdconfiguration.mddatabase-operations.mddatabase-support.mddiff-comparison.mdexceptions.mdindex.md

diff-comparison.mddocs/

0

# Diff and Comparison

1

2

This document covers Liquibase's schema comparison and diff generation tools for analyzing database differences and generating change scripts.

3

4

## Imports

5

6

```java { .api }

7

import liquibase.diff.DiffResult;

8

import liquibase.diff.DiffGeneratorFactory;

9

import liquibase.diff.compare.CompareControl;

10

import liquibase.diff.compare.SchemaComparison;

11

import liquibase.diff.ObjectDifferences;

12

13

// Database structure objects

14

import liquibase.structure.DatabaseObject;

15

import liquibase.structure.core.*;

16

17

// Database connections

18

import liquibase.database.Database;

19

import liquibase.CatalogAndSchema;

20

21

// Exception handling

22

import liquibase.exception.DatabaseException;

23

import liquibase.exception.LiquibaseException;

24

25

import java.util.Set;

26

import java.util.Map;

27

```

28

29

## DiffResult Class

30

31

The DiffResult contains the results of comparing two database schemas.

32

33

### Result Access Methods

34

35

```java { .api }

36

/**

37

* Get objects that exist in reference database but not in target

38

* @return Set of missing database objects

39

*/

40

public Set<DatabaseObject> getMissingObjects()

41

42

/**

43

* Get objects that exist in target database but not in reference

44

* @return Set of unexpected database objects

45

*/

46

public Set<DatabaseObject> getUnexpectedObjects()

47

48

/**

49

* Get objects that exist in both databases but have differences

50

* @return Map of changed objects to their differences

51

*/

52

public Map<DatabaseObject, ObjectDifferences> getChangedObjects()

53

54

/**

55

* Check if the databases are identical

56

* @return true if no differences found

57

*/

58

public boolean areEqual()

59

```

60

61

### Filtered Result Access

62

63

```java { .api }

64

/**

65

* Get missing objects of specific type

66

* @param type Database object type class

67

* @return Set of missing objects of specified type

68

*/

69

public <T extends DatabaseObject> Set<T> getMissingObjects(Class<T> type)

70

71

/**

72

* Get unexpected objects of specific type

73

* @param type Database object type class

74

* @return Set of unexpected objects of specified type

75

*/

76

public <T extends DatabaseObject> Set<T> getUnexpectedObjects(Class<T> type)

77

78

/**

79

* Get changed objects of specific type

80

* @param type Database object type class

81

* @return Map of changed objects to differences for specified type

82

*/

83

public <T extends DatabaseObject> Map<T, ObjectDifferences> getChangedObjects(Class<T> type)

84

```

85

86

## CompareControl Class

87

88

The CompareControl class controls what gets compared during diff operations.

89

90

### Constructor

91

92

```java { .api }

93

/**

94

* Create comparison control with schema mappings and object types

95

* @param schemaComparisons Array of schema comparison mappings

96

* @param compareTypes Set of database object types to compare

97

*/

98

public CompareControl(SchemaComparison[] schemaComparisons,

99

Set<Class<? extends DatabaseObject>> compareTypes)

100

```

101

102

### Access Methods

103

104

```java { .api }

105

/**

106

* Get the database object types being compared

107

* @return Set of DatabaseObject classes

108

*/

109

public Set<Class<? extends DatabaseObject>> getComparedTypes()

110

111

/**

112

* Get schema comparison mappings

113

* @return Array of SchemaComparison objects

114

*/

115

public SchemaComparison[] getSchemaComparisons()

116

```

117

118

## SchemaComparison Class

119

120

Defines mapping between reference and target schemas for comparison.

121

122

### Constructor and Methods

123

124

```java { .api }

125

/**

126

* Create schema comparison mapping

127

* @param referenceSchema Reference database schema

128

* @param comparisonSchema Target database schema for comparison

129

*/

130

public SchemaComparison(CatalogAndSchema referenceSchema, CatalogAndSchema comparisonSchema)

131

132

/**

133

* Get reference schema

134

* @return Reference CatalogAndSchema

135

*/

136

public CatalogAndSchema getReferenceSchema()

137

138

/**

139

* Get comparison schema

140

* @return Comparison CatalogAndSchema

141

*/

142

public CatalogAndSchema getComparisonSchema()

143

```

144

145

## DiffGeneratorFactory Class

146

147

Factory for creating and executing diff operations.

148

149

### Core Diff Method

150

151

```java { .api }

152

/**

153

* Compare two databases

154

* @param referenceDatabase Reference database (source of truth)

155

* @param targetDatabase Target database to compare against reference

156

* @param compareControl Controls what gets compared

157

* @return DiffResult containing comparison results

158

* @throws DatabaseException if comparison fails

159

*/

160

public DiffResult compare(Database referenceDatabase,

161

Database targetDatabase,

162

CompareControl compareControl) throws DatabaseException

163

```

164

165

### Factory Access

166

167

```java { .api }

168

/**

169

* Get singleton DiffGeneratorFactory instance

170

* @return DiffGeneratorFactory instance

171

*/

172

public static DiffGeneratorFactory getInstance()

173

```

174

175

## Database Object Types

176

177

### Core Object Types for Comparison

178

179

```java { .api }

180

// Schema structure objects

181

Catalog.class // Database catalogs

182

Schema.class // Database schemas

183

184

// Table objects

185

Table.class // Database tables

186

View.class // Database views

187

Column.class // Table columns

188

189

// Constraint objects

190

PrimaryKey.class // Primary key constraints

191

ForeignKey.class // Foreign key constraints

192

UniqueConstraint.class // Unique constraints

193

Index.class // Database indexes

194

195

// Other objects

196

Sequence.class // Database sequences

197

StoredProcedure.class // Stored procedures

198

Data.class // Table data (when enabled)

199

```

200

201

## Example Usage

202

203

### Basic Database Comparison

204

205

```java { .api }

206

import liquibase.diff.DiffGeneratorFactory;

207

import liquibase.diff.DiffResult;

208

import liquibase.diff.compare.CompareControl;

209

import liquibase.diff.compare.SchemaComparison;

210

import liquibase.database.DatabaseFactory;

211

import liquibase.CatalogAndSchema;

212

213

// Connect to reference database (production)

214

Database referenceDatabase = DatabaseFactory.getInstance().openDatabase(

215

"jdbc:postgresql://prod-server:5432/myapp",

216

"readonly_user",

217

"password",

218

null, null, null

219

);

220

221

// Connect to target database (development)

222

Database targetDatabase = DatabaseFactory.getInstance().openDatabase(

223

"jdbc:postgresql://dev-server:5432/myapp",

224

"dev_user",

225

"password",

226

null, null, null

227

);

228

229

// Set up schema comparison

230

SchemaComparison[] schemaComparisons = new SchemaComparison[] {

231

new SchemaComparison(

232

new CatalogAndSchema("myapp", "public"), // reference schema

233

new CatalogAndSchema("myapp", "public") // target schema

234

)

235

};

236

237

// Define what to compare

238

Set<Class<? extends DatabaseObject>> compareTypes = new HashSet<>(Arrays.asList(

239

Table.class,

240

Column.class,

241

PrimaryKey.class,

242

ForeignKey.class,

243

Index.class,

244

View.class

245

));

246

247

CompareControl compareControl = new CompareControl(schemaComparisons, compareTypes);

248

249

// Execute comparison

250

DiffGeneratorFactory diffFactory = DiffGeneratorFactory.getInstance();

251

DiffResult diffResult = diffFactory.compare(referenceDatabase, targetDatabase, compareControl);

252

253

// Analyze results

254

System.out.println("Databases are equal: " + diffResult.areEqual());

255

```

256

257

### Analyzing Diff Results

258

259

```java { .api }

260

// Check for missing tables (in reference but not target)

261

Set<Table> missingTables = diffResult.getMissingObjects(Table.class);

262

System.out.println("Missing tables: " + missingTables.size());

263

for (Table table : missingTables) {

264

System.out.println(" - " + table.getName());

265

}

266

267

// Check for unexpected tables (in target but not reference)

268

Set<Table> unexpectedTables = diffResult.getUnexpectedObjects(Table.class);

269

System.out.println("Unexpected tables: " + unexpectedTables.size());

270

for (Table table : unexpectedTables) {

271

System.out.println(" - " + table.getName());

272

}

273

274

// Check for changed tables

275

Map<Table, ObjectDifferences> changedTables = diffResult.getChangedObjects(Table.class);

276

System.out.println("Changed tables: " + changedTables.size());

277

for (Map.Entry<Table, ObjectDifferences> entry : changedTables.entrySet()) {

278

Table table = entry.getKey();

279

ObjectDifferences differences = entry.getValue();

280

System.out.println(" - " + table.getName() + ": " + differences.hasDifferences());

281

}

282

```

283

284

### Column-Level Comparison

285

286

```java { .api }

287

// Analyze column differences

288

Set<Column> missingColumns = diffResult.getMissingObjects(Column.class);

289

for (Column column : missingColumns) {

290

System.out.println("Missing column: " +

291

column.getRelation().getName() + "." + column.getName());

292

}

293

294

Set<Column> unexpectedColumns = diffResult.getUnexpectedObjects(Column.class);

295

for (Column column : unexpectedColumns) {

296

System.out.println("Unexpected column: " +

297

column.getRelation().getName() + "." + column.getName());

298

}

299

300

Map<Column, ObjectDifferences> changedColumns = diffResult.getChangedObjects(Column.class);

301

for (Map.Entry<Column, ObjectDifferences> entry : changedColumns.entrySet()) {

302

Column column = entry.getKey();

303

ObjectDifferences differences = entry.getValue();

304

System.out.println("Changed column: " +

305

column.getRelation().getName() + "." + column.getName());

306

307

// Examine specific differences

308

if (differences.isDifferent("type")) {

309

System.out.println(" - Type changed from " +

310

differences.getReferenceValue("type") + " to " +

311

differences.getComparedValue("type"));

312

}

313

314

if (differences.isDifferent("nullable")) {

315

System.out.println(" - Nullable changed from " +

316

differences.getReferenceValue("nullable") + " to " +

317

differences.getComparedValue("nullable"));

318

}

319

}

320

```

321

322

### Constraint Comparison

323

324

```java { .api }

325

// Primary key differences

326

Set<PrimaryKey> missingPks = diffResult.getMissingObjects(PrimaryKey.class);

327

Set<PrimaryKey> unexpectedPks = diffResult.getUnexpectedObjects(PrimaryKey.class);

328

Map<PrimaryKey, ObjectDifferences> changedPks = diffResult.getChangedObjects(PrimaryKey.class);

329

330

System.out.println("Primary key differences:");

331

System.out.println(" Missing: " + missingPks.size());

332

System.out.println(" Unexpected: " + unexpectedPks.size());

333

System.out.println(" Changed: " + changedPks.size());

334

335

// Foreign key differences

336

Set<ForeignKey> missingFks = diffResult.getMissingObjects(ForeignKey.class);

337

Set<ForeignKey> unexpectedFks = diffResult.getUnexpectedObjects(ForeignKey.class);

338

339

for (ForeignKey fk : missingFks) {

340

System.out.println("Missing FK: " + fk.getName() +

341

" (" + fk.getForeignKeyTable().getName() + " -> " +

342

fk.getPrimaryKeyTable().getName() + ")");

343

}

344

345

// Index differences

346

Set<Index> missingIndexes = diffResult.getMissingObjects(Index.class);

347

Set<Index> unexpectedIndexes = diffResult.getUnexpectedObjects(Index.class);

348

349

for (Index index : missingIndexes) {

350

System.out.println("Missing index: " + index.getName() +

351

" on " + index.getRelation().getName());

352

}

353

```

354

355

### Multi-Schema Comparison

356

357

```java { .api }

358

// Compare multiple schemas

359

SchemaComparison[] multiSchemaComparisons = new SchemaComparison[] {

360

new SchemaComparison(

361

new CatalogAndSchema("prod_db", "public"),

362

new CatalogAndSchema("dev_db", "public")

363

),

364

new SchemaComparison(

365

new CatalogAndSchema("prod_db", "reporting"),

366

new CatalogAndSchema("dev_db", "reporting")

367

),

368

new SchemaComparison(

369

new CatalogAndSchema("prod_db", "audit"),

370

new CatalogAndSchema("dev_db", "audit")

371

)

372

};

373

374

CompareControl multiSchemaControl = new CompareControl(multiSchemaComparisons, compareTypes);

375

DiffResult multiSchemaDiff = diffFactory.compare(referenceDatabase, targetDatabase, multiSchemaControl);

376

377

// Results will include differences across all specified schemas

378

System.out.println("Multi-schema comparison complete");

379

System.out.println("Total missing objects: " + multiSchemaDiff.getMissingObjects().size());

380

System.out.println("Total unexpected objects: " + multiSchemaDiff.getUnexpectedObjects().size());

381

```

382

383

### Data Comparison

384

385

```java { .api }

386

// Include data in comparison (use sparingly - can be expensive)

387

Set<Class<? extends DatabaseObject>> dataCompareTypes = new HashSet<>(Arrays.asList(

388

Table.class,

389

Column.class,

390

Data.class // Include table data

391

));

392

393

CompareControl dataCompareControl = new CompareControl(schemaComparisons, dataCompareTypes);

394

DiffResult dataResult = diffFactory.compare(referenceDatabase, targetDatabase, dataCompareControl);

395

396

// Analyze data differences

397

Set<Data> missingData = dataResult.getMissingObjects(Data.class);

398

Set<Data> unexpectedData = dataResult.getUnexpectedObjects(Data.class);

399

400

System.out.println("Data row differences:");

401

System.out.println(" Missing rows: " + missingData.size());

402

System.out.println(" Unexpected rows: " + unexpectedData.size());

403

```

404

405

### Filtered Object Comparison

406

407

```java { .api }

408

// Compare only specific object types

409

Set<Class<? extends DatabaseObject>> tableOnlyTypes = new HashSet<>(Arrays.asList(

410

Table.class,

411

Column.class

412

));

413

414

CompareControl tableOnlyControl = new CompareControl(schemaComparisons, tableOnlyTypes);

415

DiffResult tableOnlyResult = diffFactory.compare(referenceDatabase, targetDatabase, tableOnlyControl);

416

417

// Only table and column differences will be included

418

System.out.println("Table-only comparison:");

419

System.out.println("Tables equal: " + tableOnlyResult.areEqual());

420

421

// Compare only indexes

422

Set<Class<? extends DatabaseObject>> indexOnlyTypes = new HashSet<>(Arrays.asList(

423

Index.class

424

));

425

426

CompareControl indexOnlyControl = new CompareControl(schemaComparisons, indexOnlyTypes);

427

DiffResult indexOnlyResult = diffFactory.compare(referenceDatabase, targetDatabase, indexOnlyControl);

428

429

System.out.println("Index-only comparison:");

430

System.out.println("Missing indexes: " + indexOnlyResult.getMissingObjects(Index.class).size());

431

```

432

433

### Complete Diff Analysis

434

435

```java { .api }

436

public class DatabaseDiffAnalyzer {

437

438

public void analyzeDifferences(DiffResult diffResult) {

439

System.out.println("=== Database Comparison Report ===");

440

System.out.println("Databases are equal: " + diffResult.areEqual());

441

System.out.println();

442

443

analyzeStructuralDifferences(diffResult);

444

analyzeConstraintDifferences(diffResult);

445

analyzeIndexDifferences(diffResult);

446

analyzeViewDifferences(diffResult);

447

}

448

449

private void analyzeStructuralDifferences(DiffResult diffResult) {

450

System.out.println("--- Structural Differences ---");

451

452

// Schema differences

453

reportDifferences(diffResult, Schema.class, "Schemas");

454

455

// Table differences

456

reportDifferences(diffResult, Table.class, "Tables");

457

458

// Column differences

459

reportDifferences(diffResult, Column.class, "Columns");

460

}

461

462

private void analyzeConstraintDifferences(DiffResult diffResult) {

463

System.out.println("--- Constraint Differences ---");

464

465

reportDifferences(diffResult, PrimaryKey.class, "Primary Keys");

466

reportDifferences(diffResult, ForeignKey.class, "Foreign Keys");

467

reportDifferences(diffResult, UniqueConstraint.class, "Unique Constraints");

468

}

469

470

private void analyzeIndexDifferences(DiffResult diffResult) {

471

System.out.println("--- Index Differences ---");

472

reportDifferences(diffResult, Index.class, "Indexes");

473

}

474

475

private void analyzeViewDifferences(DiffResult diffResult) {

476

System.out.println("--- View Differences ---");

477

reportDifferences(diffResult, View.class, "Views");

478

}

479

480

private <T extends DatabaseObject> void reportDifferences(

481

DiffResult diffResult, Class<T> type, String typeName) {

482

483

Set<T> missing = diffResult.getMissingObjects(type);

484

Set<T> unexpected = diffResult.getUnexpectedObjects(type);

485

Map<T, ObjectDifferences> changed = diffResult.getChangedObjects(type);

486

487

if (missing.isEmpty() && unexpected.isEmpty() && changed.isEmpty()) {

488

System.out.println(typeName + ": No differences");

489

return;

490

}

491

492

System.out.println(typeName + ":");

493

System.out.println(" Missing: " + missing.size());

494

System.out.println(" Unexpected: " + unexpected.size());

495

System.out.println(" Changed: " + changed.size());

496

497

// Report details for missing objects

498

for (T obj : missing) {

499

System.out.println(" Missing: " + getObjectDescription(obj));

500

}

501

502

// Report details for unexpected objects

503

for (T obj : unexpected) {

504

System.out.println(" Unexpected: " + getObjectDescription(obj));

505

}

506

507

// Report details for changed objects

508

for (Map.Entry<T, ObjectDifferences> entry : changed.entrySet()) {

509

System.out.println(" Changed: " + getObjectDescription(entry.getKey()));

510

}

511

512

System.out.println();

513

}

514

515

private String getObjectDescription(DatabaseObject obj) {

516

if (obj instanceof Table) {

517

Table table = (Table) obj;

518

return table.getName();

519

} else if (obj instanceof Column) {

520

Column column = (Column) obj;

521

return column.getRelation().getName() + "." + column.getName();

522

} else if (obj instanceof Index) {

523

Index index = (Index) obj;

524

return index.getName() + " on " + index.getRelation().getName();

525

}

526

// Add more object types as needed

527

return obj.toString();

528

}

529

}

530

```