Skip to content

Commit f206c82

Browse files
SpencerBinXiacyrilgdnboekkooi-lengooJellalec-rabold
authored
Data Sources for schema sequence and table (#214)
Co-authored-by: Cyril Gaudin <[email protected]> Co-authored-by: Warnar Boekkooi <[email protected]> Co-authored-by: Jean-Louis Giordano <[email protected]> Co-authored-by: Alec Rabold <[email protected]>
1 parent 8c7f87e commit f206c82

14 files changed

+1186
-0
lines changed

postgresql/data_source_helpers.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package postgresql
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
)
9+
10+
const (
11+
queryConcatKeywordWhere = "WHERE"
12+
queryConcatKeywordAnd = "AND"
13+
queryArrayKeywordAny = "ANY"
14+
queryArrayKeywordAll = "ALL"
15+
likePatternQuery = "LIKE"
16+
notLikePatternQuery = "NOT LIKE"
17+
regexPatternQuery = "~"
18+
)
19+
20+
func applyPatternMatchingToQuery(patternMatchingTarget string, d *schema.ResourceData) []string {
21+
likeAnyPatterns := d.Get("like_any_patterns").([]interface{})
22+
likeAllPatterns := d.Get("like_all_patterns").([]interface{})
23+
notLikeAllPatterns := d.Get("not_like_all_patterns").([]interface{})
24+
regexPattern := d.Get("regex_pattern").(string)
25+
26+
filters := []string{}
27+
if len(likeAnyPatterns) > 0 {
28+
filters = append(filters, generatePatternMatchingString(patternMatchingTarget, likePatternQuery, generatePatternArrayString(likeAnyPatterns, queryArrayKeywordAny)))
29+
}
30+
if len(likeAllPatterns) > 0 {
31+
filters = append(filters, generatePatternMatchingString(patternMatchingTarget, likePatternQuery, generatePatternArrayString(likeAllPatterns, queryArrayKeywordAll)))
32+
}
33+
if len(notLikeAllPatterns) > 0 {
34+
filters = append(filters, generatePatternMatchingString(patternMatchingTarget, notLikePatternQuery, generatePatternArrayString(notLikeAllPatterns, queryArrayKeywordAll)))
35+
}
36+
if regexPattern != "" {
37+
filters = append(filters, generatePatternMatchingString(patternMatchingTarget, regexPatternQuery, fmt.Sprintf("'%s'", regexPattern)))
38+
}
39+
40+
return filters
41+
}
42+
43+
func generatePatternMatchingString(patternMatchingTarget string, additionalQueryKeyword string, pattern string) string {
44+
patternMatchingFilter := fmt.Sprintf("%s %s %s", patternMatchingTarget, additionalQueryKeyword, pattern)
45+
46+
return patternMatchingFilter
47+
}
48+
49+
func applyTypeMatchingToQuery(objectKeyword string, objects []interface{}) string {
50+
var typeFilter string
51+
if len(objects) > 0 {
52+
typeFilter = fmt.Sprintf("%s = %s", objectKeyword, generatePatternArrayString(objects, queryArrayKeywordAny))
53+
}
54+
55+
return typeFilter
56+
}
57+
58+
func generatePatternArrayString(patterns []interface{}, queryArrayKeyword string) string {
59+
formattedPatterns := []string{}
60+
61+
for _, pattern := range patterns {
62+
formattedPatterns = append(formattedPatterns, fmt.Sprintf("'%s'", pattern.(string)))
63+
}
64+
return fmt.Sprintf("%s (array[%s])", queryArrayKeyword, strings.Join(formattedPatterns, ","))
65+
}
66+
67+
func finalizeQueryWithFilters(query string, queryConcatKeyword string, filters []string) string {
68+
if len(filters) > 0 {
69+
query = fmt.Sprintf("%s %s %s", query, queryConcatKeyword, strings.Join(filters, " AND "))
70+
}
71+
72+
return query
73+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package postgresql
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
)
10+
11+
var schemaQueries = map[string]string{
12+
"query_include_system_schemas": `
13+
SELECT schema_name
14+
FROM information_schema.schemata
15+
`,
16+
"query_exclude_system_schemas": `
17+
SELECT schema_name
18+
FROM information_schema.schemata
19+
WHERE schema_name NOT LIKE 'pg_%'
20+
AND schema_name <> 'information_schema'
21+
`,
22+
}
23+
24+
const schemaPatternMatchingTarget = "schema_name"
25+
26+
func dataSourcePostgreSQLDatabaseSchemas() *schema.Resource {
27+
return &schema.Resource{
28+
Read: PGResourceFunc(dataSourcePostgreSQLSchemasRead),
29+
Schema: map[string]*schema.Schema{
30+
"database": {
31+
Type: schema.TypeString,
32+
Required: true,
33+
ForceNew: true,
34+
Description: "The PostgreSQL database which will be queried for schema names",
35+
},
36+
"include_system_schemas": {
37+
Type: schema.TypeBool,
38+
Default: false,
39+
Optional: true,
40+
Description: "Determines whether to include system schemas (pg_ prefix and information_schema). 'public' will always be included.",
41+
},
42+
"like_any_patterns": {
43+
Type: schema.TypeList,
44+
Optional: true,
45+
Elem: &schema.Schema{Type: schema.TypeString},
46+
MinItems: 0,
47+
Description: "Expression(s) which will be pattern matched in the query using the PostgreSQL LIKE ANY operator",
48+
},
49+
"like_all_patterns": {
50+
Type: schema.TypeList,
51+
Optional: true,
52+
Elem: &schema.Schema{Type: schema.TypeString},
53+
MinItems: 0,
54+
Description: "Expression(s) which will be pattern matched in the query using the PostgreSQL LIKE ALL operator",
55+
},
56+
"not_like_all_patterns": {
57+
Type: schema.TypeList,
58+
Optional: true,
59+
Elem: &schema.Schema{Type: schema.TypeString},
60+
MinItems: 0,
61+
Description: "Expression(s) which will be pattern matched in the query using the PostgreSQL NOT LIKE ALL operator",
62+
},
63+
"regex_pattern": {
64+
Type: schema.TypeString,
65+
Optional: true,
66+
Description: "Expression which will be pattern matched in the query using the PostgreSQL ~ (regular expression match) operator",
67+
},
68+
"schemas": {
69+
Type: schema.TypeSet,
70+
Computed: true,
71+
Elem: &schema.Schema{Type: schema.TypeString},
72+
Set: schema.HashString,
73+
Description: "The list of PostgreSQL schemas retrieved by this data source",
74+
},
75+
},
76+
}
77+
}
78+
79+
func dataSourcePostgreSQLSchemasRead(db *DBConnection, d *schema.ResourceData) error {
80+
database := d.Get("database").(string)
81+
82+
txn, err := startTransaction(db.client, database)
83+
if err != nil {
84+
return err
85+
}
86+
defer deferredRollback(txn)
87+
88+
includeSystemSchemas := d.Get("include_system_schemas").(bool)
89+
90+
var query string
91+
var queryConcatKeyword string
92+
if includeSystemSchemas {
93+
query = schemaQueries["query_include_system_schemas"]
94+
queryConcatKeyword = queryConcatKeywordWhere
95+
} else {
96+
query = schemaQueries["query_exclude_system_schemas"]
97+
queryConcatKeyword = queryConcatKeywordAnd
98+
}
99+
100+
query = applySchemaDataSourceQueryFilters(query, queryConcatKeyword, d)
101+
102+
rows, err := txn.Query(query)
103+
if err != nil {
104+
return err
105+
}
106+
defer rows.Close()
107+
108+
schemas := []string{}
109+
for rows.Next() {
110+
var schema string
111+
112+
if err = rows.Scan(&schema); err != nil {
113+
return fmt.Errorf("could not scan schema name for database: %w", err)
114+
}
115+
schemas = append(schemas, schema)
116+
}
117+
118+
d.Set("schemas", stringSliceToSet(schemas))
119+
d.SetId(generateDataSourceSchemasID(d, database))
120+
121+
return nil
122+
}
123+
124+
func generateDataSourceSchemasID(d *schema.ResourceData, databaseName string) string {
125+
return strings.Join([]string{
126+
databaseName, strconv.FormatBool(d.Get("include_system_schemas").(bool)),
127+
generatePatternArrayString(d.Get("like_any_patterns").([]interface{}), queryArrayKeywordAny),
128+
generatePatternArrayString(d.Get("like_all_patterns").([]interface{}), queryArrayKeywordAll),
129+
generatePatternArrayString(d.Get("not_like_all_patterns").([]interface{}), queryArrayKeywordAll),
130+
d.Get("regex_pattern").(string),
131+
}, "_")
132+
}
133+
134+
func applySchemaDataSourceQueryFilters(query string, queryConcatKeyword string, d *schema.ResourceData) string {
135+
filters := []string{}
136+
filters = append(filters, applyPatternMatchingToQuery(schemaPatternMatchingTarget, d)...)
137+
138+
return finalizeQueryWithFilters(query, queryConcatKeyword, filters)
139+
}

0 commit comments

Comments
 (0)