Skip to content

Commit 56ec214

Browse files
authored
Merge pull request #289 from domaframework/feature/sql-format-official-version
Sql Format Official Version
2 parents a9a2017 + c804ac0 commit 56ec214

File tree

237 files changed

+9522
-4016
lines changed

Some content is hidden

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

237 files changed

+9522
-4016
lines changed

README.md

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Checks that bind variables are used appropriately for DAO and SQL associations.
3434
The plugin also provides quick fixes for DAO methods where the required SQL files do not exist.
3535

3636
- Quick fix to generate SQL template file.
37+
3738
![quickfix.png](images/quickfix.png)
3839
- Checks for unused DAO method arguments.
3940
![inspection.png](images/inspection.png)
@@ -69,17 +70,78 @@ Along with DAO changes, the plugin will refactor the SQL file directory and file
6970
- After refactoring the DAO package, the SQL directory will also be updated.
7071
![RenameDao.gif](images/gif/RenameDao.gif)
7172

72-
## Formatter (Preview)
73+
## Formatter
7374
Provides code formatting for SQL syntax.
7475
This feature is in preview. You cannot customize the indentation or keywords to be broken down!
7576

77+
The formatting applies to SQL written in SQL files and in value fields (text blocks) within org.seasar.doma.Sql annotations.
78+
79+
![Format.gif](images/gif/Format.gif)
80+
7681
Automatic indentation on newlines provided by the SQL formatting feature is disabled by default.
7782

7883
To enable auto-indentation, toggle the corresponding flag in the settings screen below.
7984

8085
`Settings > Other Settings > Doma Tools > Enable auto-indent for SQL`
8186

82-
![Format.gif](images/gif/Format.gif)
87+
### Limitations
88+
The current formatter has the following limitations:
89+
90+
- **No customizable indentation**: The number of spaces for indentation is fixed and cannot be changed
91+
- **No customizable line breaks**: You cannot configure which keywords trigger line breaks
92+
- **No custom function registration**: User-defined functions cannot be registered for formatting rules
93+
- **No formatting style options**: Cannot choose between different formatting styles or conventions
94+
95+
### Formatting Features
96+
The SQL formatter supports the following formatting capabilities:
97+
98+
- **SQL Keywords**: Converts keywords to uppercase (SELECT, FROM, WHERE, INSERT, UPDATE, DELETE, etc.)
99+
- **Statement Structure**: Properly formats different SQL statement types:
100+
- SELECT statements with proper column and clause alignment
101+
- INSERT statements with formatted column lists and VALUES clauses
102+
- UPDATE statements with aligned SET clauses
103+
- DELETE statements with formatted conditions
104+
- WITH clauses (CTE) with proper indentation
105+
- **Joins**: Formats JOIN operations with appropriate indentation and alignment
106+
- **Subqueries**: Properly indents nested queries and subselects
107+
- **Functions**: Formats function calls with proper parameter alignment
108+
- **Comments**: Preserves single-line (--) and multi-line (/* */) comments
109+
- **Doma Directives**: Maintains proper formatting for Doma-specific directives:
110+
- Bind variables: `/* paramName */` with proper spacing
111+
- Conditional directives: `/*%if condition */` ... `/*%end*/`
112+
- Loop directives: `/*%for item : collection */` ... `/*%end*/`
113+
- Expand directive: `/*%expand */`
114+
- Populate directive: `/*%populate */`
115+
- Static property calls: `/* @ClassName@property */`
116+
- Literal values: `/*^ literalValue */`
117+
- Embedded variables: `/*# variable */`
118+
119+
### Examples
120+
121+
**Before formatting:**
122+
```sql
123+
SELECT COUNT(DISTINCT x) AS count_x, o.*, COALESCE(nbor.nearest, 999)
124+
AS nearest FROM ( SELECT p.objid, p.psfmag_g - p.extinction_g AS rpm
125+
FROM phototag p JOIN usno u ON p.objid = u.objid
126+
WHERE p.TYPE = 'Star' /*%if status == 2 */ and u.propermotion > 2.0 /*%end*/ ) as o
127+
```
128+
129+
**After formatting:**
130+
```sql
131+
SELECT COUNT(DISTINCT x) AS count_x
132+
, o.*
133+
, COALESCE(nbor.nearest
134+
, 999) AS nearest
135+
FROM ( SELECT p.objid
136+
, p.psfmag_g - p.extinction_g AS rpm
137+
FROM phototag p
138+
JOIN usno u
139+
ON p.objid = u.objid
140+
WHERE p.TYPE = 'Star'
141+
/*%if status == 2 */
142+
AND u.propermotion > 2.0
143+
/*%end*/ ) AS o
144+
```
83145

84146
## Reference Contributor
85147
Ctrl+Click on a bind variable in an SQL file to jump to its source symbol.

src/main/java/org/domaframework/doma/intellij/Sql.bnf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ private item ::= (comment | literal | word |
9494
BOOLEAN )
9595
private comment ::= (block_comment | LINE_COMMENT)
9696
private literal ::= (STRING | NUMBER)
97-
private word ::= (KEYWORD | WORD | DATATYPE)
97+
private word ::= (KEYWORD | WORD | FUNCTION_NAME | DATATYPE)
9898
block_comment ::= "/*" (el_directive | BLOCK_COMMENT_CONTENT?) "*/" {
9999
pin=1
100100
mixin="org.domaframework.doma.intellij.psi.SqlElCommentExprImpl"

src/main/java/org/domaframework/doma/intellij/Sql.flex

Lines changed: 16 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import com.intellij.lexer.FlexLexer;
77

88
import com.intellij.psi.TokenType;
99
import com.intellij.psi.tree.IElementType;
10+
import org.domaframework.doma.intellij.SqlTokenHelper;
1011

12+
import org.domaframework.doma.intellij.psi.SqlTokenType;
1113
import org.domaframework.doma.intellij.psi.SqlTypes;
1214

1315
%%
@@ -19,117 +21,19 @@ import org.domaframework.doma.intellij.psi.SqlTypes;
1921
%function advance
2022
%type IElementType
2123
%{
22-
// SQL keywords
23-
private static final Set<String> KEYWORDS = Set.of(
24-
"add",
25-
"after",
26-
"alter",
27-
"all",
28-
"and",
29-
"as",
30-
"asc",
31-
"between",
32-
"by",
33-
"case",
34-
"change",
35-
"check",
36-
"column",
37-
"comment",
38-
"create",
39-
"cross",
40-
"database",
41-
"default",
42-
"delete",
43-
"desc",
44-
"distinct",
45-
"drop",
46-
"else",
47-
"end",
48-
"except",
49-
"exists",
50-
"first",
51-
"foreign",
52-
"from",
53-
"full",
54-
"group",
55-
"having",
56-
"if",
57-
"in",
58-
"index",
59-
"inner",
60-
"insert",
61-
"intersect",
62-
"into",
63-
"is",
64-
"join",
65-
"key",
66-
"left",
67-
"like",
68-
"limit",
69-
"not",
70-
"null",
71-
"modify",
72-
"offset",
73-
"on",
74-
"outer",
75-
"or",
76-
"order",
77-
"primary",
78-
"references",
79-
"rename",
80-
"right",
81-
"select",
82-
"set",
83-
"table",
84-
"temporary",
85-
"then",
86-
"to",
87-
"truncate",
88-
"union",
89-
"unique",
90-
"update",
91-
"values",
92-
"view",
93-
"when",
94-
"where"
95-
);
96-
97-
// COLUMN DataTypes
98-
private static final Set<String> DATATYPES = Set.of(
99-
"int",
100-
"integer",
101-
"smallint",
102-
"bigint",
103-
"tinyint",
104-
"float",
105-
"double",
106-
"decimal",
107-
"numeric",
108-
"char",
109-
"varchar",
110-
"text",
111-
"date",
112-
"time",
113-
"timestamp",
114-
"datetime",
115-
"boolean",
116-
"bit",
117-
"binary",
118-
"varbinary",
119-
"blob",
120-
"clob",
121-
"json",
122-
"enum",
123-
"set"
124-
);
125-
126-
private static boolean isKeyword(CharSequence word) {
127-
return KEYWORDS.contains(word.toString().toLowerCase());
128-
}
129-
130-
private static boolean isColumnDataType(CharSequence word) {
131-
return DATATYPES.contains(word.toString().toLowerCase());
132-
}
24+
private static boolean isKeyword(CharSequence word) {
25+
// TODO Reads plugin settings and allows users to register arbitrary keywords
26+
return SqlTokenHelper.getKeyword().contains(word.toString().toLowerCase());
27+
}
28+
29+
private static boolean isColumnDataType(CharSequence word) {
30+
return SqlTokenHelper.getDataTypeTokens().contains(word.toString().toLowerCase());
31+
}
32+
33+
private static boolean isWindowFunction(CharSequence word) {
34+
// TODO Reads plugin settings and allows users to register arbitrary function names
35+
return SqlTokenHelper.getFunctionTokens().contains(word.toString().toLowerCase());
36+
}
13337
%}
13438

13539
%eof{ return;
@@ -166,7 +70,7 @@ El_NonWordPart = [=<>\-,/*();\R \n\t\f]
16670
{LineComment} { return SqlTypes.LINE_COMMENT; }
16771
{String} { return SqlTypes.STRING; }
16872
{Number} { return SqlTypes.NUMBER; }
169-
{Word} { return isKeyword(yytext()) ? SqlTypes.KEYWORD : isColumnDataType(yytext()) ? SqlTypes.DATATYPE : SqlTypes.WORD; }
73+
{Word} { return isKeyword(yytext()) ? SqlTypes.KEYWORD : isColumnDataType(yytext()) ? SqlTypes.DATATYPE : isWindowFunction(yytext()) ? SqlTypes.FUNCTION_NAME : SqlTypes.WORD; }
17074
"." { return SqlTypes.DOT; }
17175
"," { return SqlTypes.COMMA; }
17276
"+" { return SqlTypes.PLUS;}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij;
17+
18+
import java.util.HashSet;
19+
import java.util.Set;
20+
import org.domaframework.doma.intellij.tokens.MySqlFunctionToken;
21+
import org.domaframework.doma.intellij.tokens.OracleFunctionToken;
22+
import org.domaframework.doma.intellij.tokens.PostgresSqlFunctionToken;
23+
import org.domaframework.doma.intellij.tokens.SqlDataTypeTokenUtil;
24+
import org.domaframework.doma.intellij.tokens.SqlFunctionToken;
25+
import org.domaframework.doma.intellij.tokens.SqlKeywordTokenUtil;
26+
27+
public class SqlTokenHelper {
28+
29+
static final Set<String> KEYWORD_TOKENS = new HashSet<>();
30+
static final Set<String> DATA_TYPE_TOKENS = new HashSet<>();
31+
static final Set<String> FUNCTION_TOKENS = new HashSet<>();
32+
33+
static {
34+
// Initialize keyword tokens
35+
KEYWORD_TOKENS.addAll(SqlKeywordTokenUtil.getTokens());
36+
37+
// Initialize DataType tokens
38+
DATA_TYPE_TOKENS.addAll(SqlDataTypeTokenUtil.getTokens());
39+
40+
// Initialize function tokens
41+
FUNCTION_TOKENS.addAll(SqlFunctionToken.getTokens());
42+
FUNCTION_TOKENS.addAll(PostgresSqlFunctionToken.getTokens());
43+
FUNCTION_TOKENS.addAll(MySqlFunctionToken.getTokens());
44+
FUNCTION_TOKENS.addAll(OracleFunctionToken.getTokens());
45+
}
46+
47+
// Keywords
48+
public static Set<String> getKeyword() {
49+
return KEYWORD_TOKENS;
50+
}
51+
52+
// Data Types
53+
public static Set<String> getDataTypeTokens() {
54+
return DATA_TYPE_TOKENS;
55+
}
56+
57+
// Functions
58+
public static Set<String> getFunctionTokens() {
59+
return FUNCTION_TOKENS;
60+
}
61+
}

0 commit comments

Comments
 (0)