1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shardingsphere.data.pipeline.postgresql.ddlgenerator;
19
20 import org.apache.shardingsphere.data.pipeline.postgresql.util.PostgreSQLPipelineFreemarkerManager;
21 import org.postgresql.jdbc.PgArray;
22
23 import java.sql.Connection;
24 import java.sql.SQLException;
25 import java.util.Collection;
26 import java.util.LinkedHashMap;
27 import java.util.LinkedList;
28 import java.util.Map;
29
30
31
32
33 public final class PostgreSQLIndexSQLGenerator extends AbstractPostgreSQLDDLAdapter {
34
35 private static final Integer PG_INDEX_INCLUDE_VERSION = 11;
36
37 public PostgreSQLIndexSQLGenerator(final Connection connection, final int majorVersion, final int minorVersion) {
38 super(connection, majorVersion, minorVersion);
39 }
40
41
42
43
44
45
46
47
48 public String generate(final Map<String, Object> context) throws SQLException {
49 StringBuilder result = new StringBuilder();
50 Collection<Map<String, Object>> indexNodes = getIndexNodes(context);
51 for (Map<String, Object> each : indexNodes) {
52 if (each.containsKey("is_inherited") && (Boolean) each.get("is_inherited")) {
53 continue;
54 }
55 result.append(getIndexSql(context, each));
56 }
57 return result.toString().trim();
58 }
59
60 private Collection<Map<String, Object>> getIndexNodes(final Map<String, Object> context) {
61 Map<String, Object> param = new LinkedHashMap<>();
62 param.put("tid", context.get("tid"));
63 return executeByTemplate(param, "component/indexes/%s/nodes.ftl");
64 }
65
66 private String getIndexSql(final Map<String, Object> context, final Map<String, Object> indexNode) throws SQLException {
67 Map<String, Object> indexData = getIndexData(context, indexNode);
68 appendColumnDetails(indexData, (Long) indexNode.get("oid"));
69 if (getMajorVersion() >= PG_INDEX_INCLUDE_VERSION) {
70 appendIncludeDetails(indexData, (Long) indexNode.get("oid"));
71 }
72 return doGenerateIndexSql(indexData);
73 }
74
75 private String doGenerateIndexSql(final Map<String, Object> indexData) {
76 String result = PostgreSQLPipelineFreemarkerManager.getSQLByVersion(indexData, "component/indexes/%s/create.ftl", getMajorVersion(), getMinorVersion());
77 result += System.lineSeparator();
78 result += PostgreSQLPipelineFreemarkerManager.getSQLByVersion(indexData, "component/indexes/%s/alter.ftl", getMajorVersion(), getMinorVersion());
79 return result;
80 }
81
82 private Map<String, Object> getIndexData(final Map<String, Object> context, final Map<String, Object> indexNode) {
83 Collection<Map<String, Object>> indexProps = fetchIndexProperties(context, indexNode);
84 Map<String, Object> result = indexProps.iterator().next();
85 result.put("schema", context.get("schema"));
86 result.put("table", context.get("name"));
87 return result;
88 }
89
90 private Collection<Map<String, Object>> fetchIndexProperties(final Map<String, Object> context, final Map<String, Object> indexNode) {
91 Map<String, Object> param = new LinkedHashMap<>();
92 param.put("did", context.get("did"));
93 param.put("tid", context.get("tid"));
94 param.put("idx", indexNode.get("oid"));
95 param.put("datlastsysoid", context.get("datlastsysoid"));
96 return executeByTemplate(param, "component/indexes/%s/properties.ftl");
97 }
98
99 private void appendColumnDetails(final Map<String, Object> indexData, final Long indexId) throws SQLException {
100 Collection<Map<String, Object>> columnDetails = fetchColumnDetails(indexId);
101 Collection<Map<String, Object>> columns = new LinkedList<>();
102 Collection<String> columnDisplays = new LinkedList<>();
103 for (Map<String, Object> each : columnDetails) {
104 columns.add(getColumnData(indexData, each));
105 columnDisplays.add(getColumnPropertyDisplayData(each, indexData));
106 }
107 indexData.put("columns", columns);
108 indexData.put("columns_csv", String.join(", ", columnDisplays));
109 }
110
111 private Map<String, Object> getColumnData(final Map<String, Object> indexData, final Map<String, Object> columnDetail) throws SQLException {
112 Map<String, Object> result = new LinkedHashMap<>();
113 result.put("colname", columnDetail.get("attdef"));
114 result.put("collspcname", columnDetail.get("collnspname"));
115 result.put("op_class", columnDetail.get("opcname"));
116 if ("btree".equals(indexData.get("amname"))) {
117 result.put("sort_order", isSortOrder(columnDetail));
118 result.put("nulls", isNulls(columnDetail));
119 }
120 return result;
121 }
122
123 private boolean isSortOrder(final Map<String, Object> columnDetail) throws SQLException {
124 if (null == columnDetail.get("options")) {
125 return false;
126 }
127 String[] options = (String[]) ((PgArray) columnDetail.get("options")).getArray();
128 return options.length > 0 && "DESC".equals(options[0]);
129 }
130
131 private Object isNulls(final Map<String, Object> columnDetail) throws SQLException {
132 if (null == columnDetail.get("options")) {
133 return false;
134 }
135 String[] options = (String[]) ((PgArray) columnDetail.get("options")).getArray();
136 return options.length > 1 && options[1].split(" ").length > 1 && "FIRST".equals(options[1].split(" ")[1]);
137 }
138
139 private Collection<Map<String, Object>> fetchColumnDetails(final Long indexId) {
140 Map<String, Object> param = new LinkedHashMap<>();
141 param.put("idx", indexId);
142 return executeByTemplate(param, "component/indexes/%s/column_details.ftl");
143 }
144
145 private String getColumnPropertyDisplayData(final Map<String, Object> columnDetail, final Map<String, Object> indexData) throws SQLException {
146 String result = (String) columnDetail.get("attdef");
147 if (null != columnDetail.get("collnspname")) {
148 result += " COLLATE " + columnDetail.get("collnspname");
149 }
150 if (null != columnDetail.get("opcname")) {
151 result += " " + columnDetail.get("opcname");
152 }
153 if ("btree".equals(indexData.get("amname"))) {
154 String[] options = (String[]) ((PgArray) columnDetail.get("options")).getArray();
155 if (options.length > 0) {
156 result += " " + options[0];
157 }
158 if (options.length > 1) {
159 result += " " + options[1];
160 }
161 }
162 return result;
163 }
164
165 private void appendIncludeDetails(final Map<String, Object> indexData, final Long oid) {
166 Map<String, Object> param = new LinkedHashMap<>();
167 param.put("idx", oid);
168 Collection<Object> includes = new LinkedList<>();
169 for (Map<String, Object> each : executeByTemplate(param, "component/indexes/%s/include_details.ftl")) {
170 includes.add(each.get("colname"));
171 }
172 indexData.put("include", includes);
173 }
174 }