1
+ package cc.unitmesh.database.util
2
+
3
+ import com.intellij.database.console.JdbcConsole
4
+ import com.intellij.database.console.JdbcConsoleProvider
5
+ import com.intellij.database.console.evaluation.EvaluationRequest
6
+ import com.intellij.database.console.session.DatabaseSession
7
+ import com.intellij.database.console.session.getSessionTitle
8
+ import com.intellij.database.datagrid.GridDataRequest
9
+ import com.intellij.database.datagrid.GridRow
10
+ import com.intellij.database.model.RawDataSource
11
+ import com.intellij.database.script.PersistenceConsoleProvider
12
+ import com.intellij.database.settings.DatabaseSettings
13
+ import com.intellij.database.vfs.DbVFSUtils
14
+ import com.intellij.openapi.application.ApplicationManager
15
+ import com.intellij.openapi.application.runInEdt
16
+ import com.intellij.openapi.application.runReadAction
17
+ import com.intellij.openapi.editor.ex.EditorEx
18
+ import com.intellij.openapi.fileEditor.FileEditorManager
19
+ import com.intellij.openapi.project.Project
20
+ import com.intellij.psi.PsiFile
21
+ import com.intellij.psi.PsiManager
22
+ import com.intellij.sql.psi.SqlPsiFacade
23
+ import com.intellij.testFramework.LightVirtualFile
24
+ import com.intellij.util.Consumer
25
+ import java.util.concurrent.CompletableFuture
26
+
27
+ object SQLExecutor {
28
+ fun executeSqlQuery (project : Project , sql : String ): String {
29
+ val file = LightVirtualFile (" temp.sql" , sql)
30
+ val psiFile = runReadAction { PsiManager .getInstance(project).findFile(file) }
31
+ ? : return " ShireError[Database]: Can't find PSI file"
32
+
33
+ val dataSource = DatabaseSchemaAssistant .allRawDatasource(project).firstOrNull()
34
+ ? : throw IllegalArgumentException (" ShireError[Database]: No database found" )
35
+
36
+ val execOptions = DatabaseSettings .getSettings().execOptions.last()
37
+ val console: JdbcConsole ? = JdbcConsole .getActiveConsoles(project).firstOrNull()
38
+ ? : JdbcConsoleProvider .getValidConsole(project, file)
39
+ ? : createConsole(project, file)
40
+
41
+ if (console != null ) {
42
+ return executeSqlInConsole(console, sql, dataSource)
43
+ }
44
+
45
+ val editor = FileEditorManager .getInstance(project).selectedTextEditor
46
+ ? : throw IllegalArgumentException (" ShireError[Database]: No editor found" )
47
+
48
+ val scriptModel = SqlPsiFacade .getInstance(project).createScriptModel(psiFile)
49
+
50
+ val info = JdbcConsoleProvider .Info (psiFile, psiFile, editor as EditorEx , scriptModel, execOptions, null )
51
+ val runners: MutableList <PersistenceConsoleProvider .Runner > = runReadAction {
52
+ getAttachDataSourceRunnersReflect(info)
53
+ }
54
+
55
+ if (runners.size == 1 ) {
56
+ var result = " "
57
+ runInEdt {
58
+ result = executeSql(project, dataSource, sql) ? : " Error"
59
+ }
60
+
61
+ return result
62
+ } else {
63
+ try {
64
+ val chooseAndRunRunnersMethod =
65
+ Class .forName(" com.intellij.database.intentions.RunQueryInConsoleIntentionAction\$ Manager" )
66
+ .getDeclaredMethod(
67
+ " chooseAndRunRunners" ,
68
+ List ::class .java,
69
+ EditorEx ::class .java,
70
+ Any ::class .java
71
+ )
72
+ chooseAndRunRunnersMethod.invoke(null , runners, info.editor, null )
73
+ } catch (e: Exception ) {
74
+ println (" ShireError[Database]: Failed to run query with multiple runners" )
75
+ throw e
76
+ }
77
+ return " ShireError[Database]: Currently not support multiple runners"
78
+ }
79
+ }
80
+
81
+ // private fun getAttachDataSourceRunners(info: JdbcConsoleProvider.Info): MutableList<PersistenceConsoleProvider.Runner> {
82
+ // val virtualFile = info.editor!!.virtualFile
83
+ // val project = info.originalFile.project
84
+ // val title = getSessionTitle(virtualFile)
85
+ // val consumer: Consumer<in DatabaseSession> =
86
+ // Consumer<DatabaseSession> { newSession: DatabaseSession? ->
87
+ // val console = JdbcConsoleProvider.attachConsole(
88
+ // project,
89
+ // newSession!!, virtualFile
90
+ // )
91
+ // if (console != null) {
92
+ // val runnable = Runnable { JdbcConsoleProvider.doRunQueryInConsole(console, info) }
93
+ // if (DbVFSUtils.isAssociatedWithDataSourceAndSchema(virtualFile)) {
94
+ // runnable.run()
95
+ // } else {
96
+ // DatabaseRunners.chooseSchemaAndRun(info.editor!!, runnable)
97
+ // }
98
+ // }
99
+ // }
100
+ //
101
+ // return DatabaseRunners.getAttachDataSourceRunners(info.file, title, consumer)
102
+ // }
103
+
104
+ private fun getAttachDataSourceRunnersReflect (info : JdbcConsoleProvider .Info ): MutableList <PersistenceConsoleProvider .Runner > {
105
+ val virtualFile = info.editor!! .virtualFile
106
+ val project = info.originalFile.project
107
+ val title = getSessionTitle(virtualFile)
108
+ val consumer: Consumer <DatabaseSession > = Consumer <DatabaseSession > { newSession: DatabaseSession ? ->
109
+ val console = JdbcConsoleProvider .attachConsole(project, newSession!! , virtualFile)
110
+ if (console != null ) {
111
+ val runnable = Runnable { JdbcConsoleProvider .doRunQueryInConsole(console, info) }
112
+ try {
113
+ // 使用反射调用 DbVFSUtils.isAssociatedWithDataSourceAndSchema
114
+ val isAssociatedMethod = DbVFSUtils ::class .java.getDeclaredMethod(
115
+ " isAssociatedWithDataSourceAndSchema" ,
116
+ virtualFile::class .java
117
+ )
118
+ val isAssociated = isAssociatedMethod.invoke(null , virtualFile) as Boolean
119
+
120
+ if (isAssociated) {
121
+ runnable.run ()
122
+ } else {
123
+ val chooseSchemaMethod = Class .forName(" com.intellij.database.console.DatabaseRunners" )
124
+ .getDeclaredMethod(" chooseSchemaAndRun" , EditorEx ::class .java, Runnable ::class .java)
125
+ chooseSchemaMethod.invoke(null , info.editor, runnable)
126
+ }
127
+ } catch (e: Exception ) {
128
+ println (" ShireError[Database]: Failed to run query in console" )
129
+ throw e
130
+ }
131
+ }
132
+ }
133
+
134
+ try {
135
+ // 使用反射调用 DatabaseRunners.getAttachDataSourceRunners
136
+ val getRunners = Class .forName(" com.intellij.database.console.DatabaseRunners" )
137
+ .getDeclaredMethod(
138
+ " getAttachDataSourceRunners" ,
139
+ PsiFile ::class .java,
140
+ String ::class .java,
141
+ Consumer ::class .java
142
+ )
143
+ @Suppress(" UNCHECKED_CAST" )
144
+ return getRunners.invoke(null , info.file, title, consumer) as MutableList <PersistenceConsoleProvider .Runner >
145
+ } catch (e: Exception ) {
146
+ println (" ShireError[Database]: Failed to get runners" )
147
+ throw e
148
+ }
149
+ }
150
+
151
+ private fun executeSqlInConsole (console : JdbcConsole , sql : String , dataSource : RawDataSource ): String {
152
+ val future: CompletableFuture <String > = CompletableFuture ()
153
+ return ApplicationManager .getApplication().executeOnPooledThread<String > {
154
+ val messageBus = console.session.messageBus
155
+ val newConsoleRequest = EvaluationRequest .newRequest(console, sql, dataSource.dbms)
156
+ messageBus.dataProducer.processRequest(newConsoleRequest)
157
+ messageBus.addConsumer(object : MyCompatDataConsumer () {
158
+ var result = mutableListOf<GridRow >()
159
+ override fun addRows (context : GridDataRequest .Context , rows : MutableList <out GridRow >) {
160
+ result + = rows
161
+ if (rows.size < 100 ) {
162
+ future.complete(result.toString())
163
+ }
164
+ }
165
+ })
166
+ return @executeOnPooledThread future.get()
167
+ }.get()
168
+ }
169
+
170
+ private fun executeSql (project : Project , dataSource : RawDataSource , query : String ): String? {
171
+ val future: java.util.concurrent.CompletableFuture <String > = java.util.concurrent.CompletableFuture ()
172
+ val localDs = com.intellij.database.util.DbImplUtilCore .getLocalDataSource(dataSource)
173
+
174
+ val session = com.intellij.database.console.session.DatabaseSessionManager .getSession(project, localDs)
175
+ val messageBus = session.messageBus
176
+ messageBus.addConsumer(object : MyCompatDataConsumer () {
177
+ var result = mutableListOf< com.intellij.database.datagrid.GridRow > ()
178
+ override fun addRows (context : com.intellij.database.datagrid.GridDataRequest .Context , rows : MutableList <out com.intellij.database.datagrid.GridRow >) {
179
+ result + = rows
180
+ if (rows.size < 100 ) {
181
+ future.complete(result.toString())
182
+ }
183
+ }
184
+ })
185
+
186
+ val request =
187
+ object : com.intellij.database.datagrid.DataRequest .QueryRequest (session, query,
188
+ newConstraints(dataSource.dbms), null ) {}
189
+ messageBus.dataProducer.processRequest(request)
190
+ return future.get()
191
+ }
192
+
193
+ private fun createConsole (project : com.intellij.openapi.project.Project , file : com.intellij.testFramework.LightVirtualFile ): com.intellij.database.console.JdbcConsole ? {
194
+ val attached = com.intellij.database.console.JdbcConsoleProvider .findOrCreateSession(project, file) ? : return null
195
+ return com.intellij.database.console.JdbcConsoleProvider .attachConsole(project, attached, file)
196
+ }
197
+
198
+ abstract class MyCompatDataConsumer : com.intellij.database.datagrid.DataConsumer {
199
+ override fun setColumns (
200
+ context : com.intellij.database.datagrid.GridDataRequest .Context ,
201
+ subQueryIndex : Int ,
202
+ resultSetIndex : Int ,
203
+ columns : Array <out com.intellij.database.datagrid.GridColumn >,
204
+ firstRowNum : Int ,
205
+ ) {
206
+ // for Compatibility in IDEA 2023.2.8
207
+ }
208
+
209
+ override fun afterLastRowAdded (context : com.intellij.database.datagrid.GridDataRequest .Context , total : Int ) {
210
+ // for Compatibility in IDEA 2023.2.8
211
+ }
212
+ }
213
+ }
0 commit comments