diff --git a/docs/mooseide-integration.md b/docs/mooseide-integration.md index 84917fe..6e38879 100644 --- a/docs/mooseide-integration.md +++ b/docs/mooseide-integration.md @@ -22,3 +22,18 @@ fork-first v3 development work, not a published v3 release. GitHub CI loads these optional tests in the ready-made Moose image. The plain Pharo job continues to test Core without GUI dependencies. + +## SQL CoUsage + +Select **SQL relation references** as the inner-box extractor in the standard +CoUsage map settings, or use: + +```smalltalk +FmxSQLCoUsageExtractor openOn: (model allWithType: FmxSQLStoredProcedure). +``` + +Containers are routines, views or tables; inner boxes are referenced database +tables/views. Column references are projected to their owning relation, and +foreign keys contribute referenced relations. Repeated references retain usage +counts. Empty results mean no modeled references, not proof that dynamic SQL has +no dependencies. The helper rejects mixed/unsupported input before opening. diff --git a/src/FamixNGSQL-MooseIDE-Tests/FmxSQLCoUsageTest.class.st b/src/FamixNGSQL-MooseIDE-Tests/FmxSQLCoUsageTest.class.st new file mode 100644 index 0000000..09ecbdd --- /dev/null +++ b/src/FamixNGSQL-MooseIDE-Tests/FmxSQLCoUsageTest.class.st @@ -0,0 +1,41 @@ +Class { + #name : #FmxSQLCoUsageTest, + #superclass : #TestCase, + #category : #'FamixNGSQL-MooseIDE-Tests' +} +{ #category : #tests } +FmxSQLCoUsageTest >> testSharedRelationsAndRepeatedReferences [ + | model table column first second extractor browser | + model := FmxSQLModel new. + table := FmxSQLTable new name: 'records'; mooseModel: model; yourself. + column := FmxSQLColumn new name: 'id'; columnsContainer: table; mooseModel: model; yourself. + first := FmxSQLStoredProcedure new name: 'first'; mooseModel: model; yourself. + second := FmxSQLStoredProcedure new name: 'second'; mooseModel: model; yourself. + FmxSQLColumnReference new source: first; column: column; mooseModel: model. + FmxSQLTableReference new source: first; table: table; mooseModel: model. + FmxSQLTableReference new source: second; table: table; mooseModel: model. + extractor := FmxSQLCoUsageExtractor new. + self assert: (extractor innerEntitiesFor: first) asArray equals: { table. table }. + self assert: (extractor innerEntitiesFor: second) asArray equals: { table }. + browser := FmxSQLCoUsageExtractor openOn: { first. second }. + [ self assert: browser specModel containerBoxes size equals: 2. + self assert: browser specModel innerBoxes size equals: 1 ] + ensure: [ browser window close ] +] +{ #category : #tests } +FmxSQLCoUsageTest >> testForeignKeyAndEmptyTable [ + | model source target column constraint | + model := FmxSQLModel new. + source := FmxSQLTable new name: 'source'; mooseModel: model; yourself. + target := FmxSQLTable new name: 'target'; mooseModel: model; yourself. + column := FmxSQLColumn new name: 'id'; columnsContainer: target; mooseModel: model; yourself. + constraint := FmxSQLForeignKeyConstraint new name: 'fk'; table: source; mooseModel: model; yourself. + FmxSQLColumnReference new foreignKeyConstraintSource: constraint; column: column; mooseModel: model. + self assert: (FmxSQLCoUsageExtractor new innerEntitiesFor: source) asArray equals: { target }. + self assertEmpty: (FmxSQLCoUsageExtractor new innerEntitiesFor: target) +] +{ #category : #tests } +FmxSQLCoUsageTest >> testUnsupportedInputIsRejectedBeforeOpening [ + self should: [ FmxSQLCoUsageExtractor openOn: { Object new } ] raise: Error. + self should: [ FmxSQLCoUsageExtractor new innerEntitiesFor: Object new ] raise: Error +] diff --git a/src/FamixNGSQL-MooseIDE/FmxSQLCoUsageExtractor.class.st b/src/FamixNGSQL-MooseIDE/FmxSQLCoUsageExtractor.class.st new file mode 100644 index 0000000..823c44b --- /dev/null +++ b/src/FamixNGSQL-MooseIDE/FmxSQLCoUsageExtractor.class.st @@ -0,0 +1,46 @@ +"Co-usage of database relations: containers are routines, views or tables; +inner boxes are referenced tables/views. Repeated references retain their frequency." +Class { + #name : #FmxSQLCoUsageExtractor, + #superclass : #MiCoInnerEntitiesAbstractExtractor, + #category : #'FamixNGSQL-MooseIDE' +} +{ #category : #accessing } +FmxSQLCoUsageExtractor class >> menuItemName [ ^ 'SQL relation references' ] +{ #category : #accessing } +FmxSQLCoUsageExtractor class >> description [ + ^ 'Referenced tables and views, including column and foreign-key references. Repeated references count as repeated uses.' +] +{ #category : #testing } +FmxSQLCoUsageExtractor class >> accepts: entity [ + ^ { FmxSQLTable. FmxSQLForeignTable. FmxSQLView. FmxSQLStoredProcedure } + anySatisfy: [ :class | entity isKindOf: class ] +] +{ #category : #opening } +FmxSQLCoUsageExtractor class >> openOn: entities [ + | browser | + (entities allSatisfy: [ :entity | self accepts: entity ]) ifFalse: [ + self error: 'SQL CoUsage expects only tables, views or routines' ]. + browser := MiCoUsageMapBrowser new. + browser specModel settings innerBoxExtractor: self. + browser open. + browser followEntity: entities asMooseGroup. + ^ browser +] +{ #category : #querying } +FmxSQLCoUsageExtractor >> innerEntitiesFor: entity [ + | relations | + (self class accepts: entity) ifFalse: [ self error: 'Unsupported SQL CoUsage entity' ]. + relations := OrderedCollection new. + entity queryAllOutgoing do: [ :reference | + ((reference isKindOf: FmxSQLColumnReference) or: [ + (reference isKindOf: FmxSQLTableReference) or: [ reference isKindOf: FmxSQLViewReference ] ]) ifTrue: [ + reference allTargets asSet do: [ :target | + | relation | + relation := (target isKindOf: FmxSQLColumn) + ifTrue: [ target columnsContainer ] ifFalse: [ target ]. + ((relation isKindOf: FmxSQLTable) or: [ + (relation isKindOf: FmxSQLForeignTable) or: [ relation isKindOf: FmxSQLView ] ]) + ifTrue: [ relations add: relation ] ] ] ]. + ^ relations +]