CreateDatabasesInPostgreSQL calls exec to create databases that do not exist in PostgreSQL.
( ctx context.Context, exec Executor, databases []string, )
| 15 | // CreateDatabasesInPostgreSQL calls exec to create databases that do not exist |
| 16 | // in PostgreSQL. |
| 17 | func CreateDatabasesInPostgreSQL( |
| 18 | ctx context.Context, exec Executor, databases []string, |
| 19 | ) error { |
| 20 | log := logging.FromContext(ctx) |
| 21 | |
| 22 | var err error |
| 23 | var sql bytes.Buffer |
| 24 | |
| 25 | // Prevent unexpected dereferences by emptying "search_path". The "pg_catalog" |
| 26 | // schema is still searched, and only temporary objects can be created. |
| 27 | // - https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-SEARCH-PATH |
| 28 | _, _ = sql.WriteString(`SET search_path TO '';`) |
| 29 | |
| 30 | // Fill a temporary table with the JSON of the database specifications. |
| 31 | // "\copy" reads from subsequent lines until the special line "\.". |
| 32 | // - https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMANDS-COPY |
| 33 | _, _ = sql.WriteString(` |
| 34 | CREATE TEMPORARY TABLE input (id serial, data json); |
| 35 | \copy input (data) from stdin with (format text) |
| 36 | `) |
| 37 | |
| 38 | encoder := json.NewEncoder(&sql) |
| 39 | encoder.SetEscapeHTML(false) |
| 40 | |
| 41 | for i := range databases { |
| 42 | if err == nil { |
| 43 | err = encoder.Encode(map[string]any{ |
| 44 | "database": databases[i], |
| 45 | }) |
| 46 | } |
| 47 | } |
| 48 | _, _ = sql.WriteString(`\.` + "\n") |
| 49 | |
| 50 | // Create databases that do not already exist. |
| 51 | // - https://www.postgresql.org/docs/current/sql-createdatabase.html |
| 52 | _, _ = sql.WriteString(` |
| 53 | SELECT pg_catalog.format('CREATE DATABASE %I', |
| 54 | pg_catalog.json_extract_path_text(input.data, 'database')) |
| 55 | FROM input |
| 56 | WHERE NOT EXISTS ( |
| 57 | SELECT 1 FROM pg_catalog.pg_database |
| 58 | WHERE datname = pg_catalog.json_extract_path_text(input.data, 'database')) |
| 59 | ORDER BY input.id |
| 60 | \gexec |
| 61 | `) |
| 62 | |
| 63 | stdout, stderr, err := exec.Exec(ctx, &sql, |
| 64 | map[string]string{ |
| 65 | "ON_ERROR_STOP": "on", // Abort when any one statement fails. |
| 66 | "QUIET": "on", // Do not print successful statements to stdout. |
| 67 | }) |
| 68 | |
| 69 | log.V(1).Info("created PostgreSQL databases", "stdout", stdout, "stderr", stderr) |
| 70 | |
| 71 | return err |
| 72 | } |