From cc5d6777f6d1116d39f8631ff631b8a2a3a71cf3 Mon Sep 17 00:00:00 2001 From: Ingars Ribners Date: Sat, 30 Nov 2024 22:08:56 +0300 Subject: [PATCH 1/9] Initial version of PostgreSQL schema generator 'postgresql-schema' (based on 'mysql-schema' generator code) --- bin/configs/postgresql-schema-petstore.yaml | 5 + docs/generators.md | 1 + docs/generators/postgresql-schema.md | 451 ++++++ .../codegen/GeneratorLanguage.java | 2 +- .../languages/PostgresqlSchemaCodegen.java | 1222 +++++++++++++++++ .../org.openapitools.codegen.CodegenConfig | 1 + .../postgresql-schema/README.mustache | 56 + .../postgresql_schema.mustache | 154 +++ .../postgresql-schema/sql_query.mustache | 27 + .../PostgresqlSchemaOptionsProvider.java | 49 + .../PostgresqlSchemaCodegenTest.java | 311 +++++ .../PostgresqlSchemaOptionsTest.java | 47 + .../postgresql/.openapi-generator-ignore | 23 + .../postgresql/.openapi-generator/FILES | 8 + .../postgresql/.openapi-generator/VERSION | 1 + .../petstore/postgresql/Model/ApiResponse.sql | 26 + .../petstore/postgresql/Model/Category.sql | 26 + .../petstore/postgresql/Model/Order.sql | 26 + .../schema/petstore/postgresql/Model/Pet.sql | 26 + .../schema/petstore/postgresql/Model/Tag.sql | 26 + .../schema/petstore/postgresql/Model/User.sql | 26 + samples/schema/petstore/postgresql/README.md | 56 + .../petstore/postgresql/postgresql_schema.sql | 190 +++ 23 files changed, 2759 insertions(+), 1 deletion(-) create mode 100644 bin/configs/postgresql-schema-petstore.yaml create mode 100644 docs/generators/postgresql-schema.md create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java create mode 100644 modules/openapi-generator/src/main/resources/postgresql-schema/README.mustache create mode 100644 modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache create mode 100644 modules/openapi-generator/src/main/resources/postgresql-schema/sql_query.mustache create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/options/PostgresqlSchemaOptionsProvider.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java create mode 100644 samples/schema/petstore/postgresql/.openapi-generator-ignore create mode 100644 samples/schema/petstore/postgresql/.openapi-generator/FILES create mode 100644 samples/schema/petstore/postgresql/.openapi-generator/VERSION create mode 100644 samples/schema/petstore/postgresql/Model/ApiResponse.sql create mode 100644 samples/schema/petstore/postgresql/Model/Category.sql create mode 100644 samples/schema/petstore/postgresql/Model/Order.sql create mode 100644 samples/schema/petstore/postgresql/Model/Pet.sql create mode 100644 samples/schema/petstore/postgresql/Model/Tag.sql create mode 100644 samples/schema/petstore/postgresql/Model/User.sql create mode 100644 samples/schema/petstore/postgresql/README.md create mode 100644 samples/schema/petstore/postgresql/postgresql_schema.sql diff --git a/bin/configs/postgresql-schema-petstore.yaml b/bin/configs/postgresql-schema-petstore.yaml new file mode 100644 index 000000000000..2ab2773849ba --- /dev/null +++ b/bin/configs/postgresql-schema-petstore.yaml @@ -0,0 +1,5 @@ +generatorName: postgresql-schema +outputDir: samples/schema/petstore/postgresql +inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml +templateDir: modules/openapi-generator/src/main/resources/postgresql-schema + diff --git a/docs/generators.md b/docs/generators.md index f3ed0d410525..66b0b018f1f4 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -168,6 +168,7 @@ The following generators are available: * [graphql-schema](generators/graphql-schema.md) * [ktorm-schema (beta)](generators/ktorm-schema.md) * [mysql-schema](generators/mysql-schema.md) +* [postgresql-schema](generators/postgresql-schema.md) * [postman-collection (beta)](generators/postman-collection.md) * [protobuf-schema (beta)](generators/protobuf-schema.md) * [wsdl-schema (beta)](generators/wsdl-schema.md) diff --git a/docs/generators/postgresql-schema.md b/docs/generators/postgresql-schema.md new file mode 100644 index 000000000000..150c32037b3f --- /dev/null +++ b/docs/generators/postgresql-schema.md @@ -0,0 +1,451 @@ +--- +title: Documentation for the postgresql-schema Generator +--- + +## METADATA + +| Property | Value | Notes | +| -------- | ----- | ----- | +| generator name | postgresql-schema | pass this to the generate command after -g | +| generator stability | STABLE | | +| generator type | SCHEMA | | +| generator language | Postgresql | | +| generator default templating engine | mustache | | +| helpTxt | Generates a PostgreSQL schema based on the model or schema defined in the OpenAPI specification (v2, v3). | | + +## CONFIG OPTIONS +These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details. + +| Option | Description | Values | Default | +| ------ | ----------- | ------ | ------- | +|defaultDatabaseName|Default database name for all PostgreSQL queries| || +|identifierNamingConvention|Naming convention of PostgreSQL identifiers(table names and column names). This is not related to database name which is defined by defaultDatabaseName option|
**original**
Do not transform original names
**snake_case**
Use snake_case names
|original| +|jsonDataTypeEnabled|Use special JSON PostgreSQL data type for complex model properties. Requires PostgreSQL version 5.7.8. Generates TEXT data type when disabled| |true| +|namedParametersEnabled|Generates model prepared SQLs with named parameters, eg. :petName. Question mark placeholder used when option disabled.| |false| + +## IMPORT MAPPING + +| Type/Alias | Imports | +| ---------- | ------- | + + +## INSTANTIATION TYPES + +| Type/Alias | Instantiated By | +| ---------- | --------------- | + + +## LANGUAGE PRIMITIVES + + + +## RESERVED WORDS + + + +## FEATURE SET + + +### Client Modification Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasePath|✗|ToolingExtension +|Authorizations|✗|ToolingExtension +|UserAgent|✗|ToolingExtension +|MockServer|✗|ToolingExtension + +### Data Type Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Custom|✗|OAS2,OAS3 +|Int32|✓|OAS2,OAS3 +|Int64|✓|OAS2,OAS3 +|Float|✓|OAS2,OAS3 +|Double|✓|OAS2,OAS3 +|Decimal|✓|ToolingExtension +|String|✓|OAS2,OAS3 +|Byte|✓|OAS2,OAS3 +|Binary|✓|OAS2,OAS3 +|Boolean|✓|OAS2,OAS3 +|Date|✓|OAS2,OAS3 +|DateTime|✓|OAS2,OAS3 +|Password|✓|OAS2,OAS3 +|File|✓|OAS2 +|Uuid|✗| +|Array|✓|OAS2,OAS3 +|Null|✗|OAS3 +|AnyType|✗|OAS2,OAS3 +|Object|✓|OAS2,OAS3 +|Maps|✓|ToolingExtension +|CollectionFormat|✓|OAS2 +|CollectionFormatMulti|✓|OAS2 +|Enum|✓|OAS2,OAS3 +|ArrayOfEnum|✓|ToolingExtension +|ArrayOfModel|✓|ToolingExtension +|ArrayOfCollectionOfPrimitives|✓|ToolingExtension +|ArrayOfCollectionOfModel|✓|ToolingExtension +|ArrayOfCollectionOfEnum|✓|ToolingExtension +|MapOfEnum|✓|ToolingExtension +|MapOfModel|✓|ToolingExtension +|MapOfCollectionOfPrimitives|✓|ToolingExtension +|MapOfCollectionOfModel|✓|ToolingExtension +|MapOfCollectionOfEnum|✓|ToolingExtension + +### Documentation Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Readme|✓|ToolingExtension +|Model|✓|ToolingExtension +|Api|✓|ToolingExtension + +### Global Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Host|✓|OAS2,OAS3 +|BasePath|✓|OAS2,OAS3 +|Info|✓|OAS2,OAS3 +|Schemes|✗|OAS2,OAS3 +|PartialSchemes|✓|OAS2,OAS3 +|Consumes|✓|OAS2 +|Produces|✓|OAS2 +|ExternalDocumentation|✓|OAS2,OAS3 +|Examples|✓|OAS2,OAS3 +|XMLStructureDefinitions|✗|OAS2,OAS3 +|MultiServer|✗|OAS3 +|ParameterizedServer|✗|OAS3 +|ParameterStyling|✗|OAS3 +|Callbacks|✗|OAS3 +|LinkObjects|✗|OAS3 + +### Parameter Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Path|✓|OAS2,OAS3 +|Query|✓|OAS2,OAS3 +|Header|✓|OAS2,OAS3 +|Body|✓|OAS2 +|FormUnencoded|✓|OAS2 +|FormMultipart|✓|OAS2 +|Cookie|✓|OAS3 + +### Schema Support Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Simple|✓|OAS2,OAS3 +|Composite|✓|OAS2,OAS3 +|Polymorphism|✗|OAS2,OAS3 +|Union|✗|OAS3 +|allOf|✗|OAS2,OAS3 +|anyOf|✗|OAS3 +|oneOf|✗|OAS3 +|not|✗|OAS3 + +### Security Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasicAuth|✗|OAS2,OAS3 +|ApiKey|✗|OAS2,OAS3 +|OpenIDConnect|✗|OAS3 +|BearerToken|✗|OAS3 +|OAuth2_Implicit|✗|OAS2,OAS3 +|OAuth2_Password|✗|OAS2,OAS3 +|OAuth2_ClientCredentials|✗|OAS2,OAS3 +|OAuth2_AuthorizationCode|✗|OAS2,OAS3 +|SignatureAuth|✗|OAS3 +|AWSV4Signature|✗|ToolingExtension + +### Wire Format Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|JSON|✗|OAS2,OAS3 +|XML|✗|OAS2,OAS3 +|PROTOBUF|✗|ToolingExtension +|Custom|✗|OAS2,OAS3 diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/GeneratorLanguage.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/GeneratorLanguage.java index a75901cd54e9..bbbd8fe1b725 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/GeneratorLanguage.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/GeneratorLanguage.java @@ -30,7 +30,7 @@ public enum GeneratorLanguage { JAVASCRIPT("Javascript"), GRAPH_QL("GraphQL"), GROOVY("Groovy"), HASKELL("Haskell"), HTTP("Jetbrains HTTP Client (HTTP/REST)"), TYPESCRIPT("Typescript"), K_SIX("k6"), KOTLIN("Kotlin"), KTORM("Ktorm"), LUA("Lua"), MYSQL("Mysql"), NIM("Nim"), - OBJECTIVE_C("Objective-C"), OCAML("OCaml"), PERL("Perl"), PHP("PHP"), + OBJECTIVE_C("Objective-C"), OCAML("OCaml"), PERL("Perl"), PHP("PHP"), POSTGRESQL("Postgresql"), POWERSHELL("PowerShell"), PROTOBUF("Protocol Buffers (Protobuf)"), PYTHON("Python"), R("R"), RUBY("Ruby"), RUST("Rust"), SCALA("Scala"), SWIFT("Swift"), WSDL("Web Services Description Language (WSDL)"), JULIA("Julia"), XOJO("Xojo"); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java new file mode 100644 index 000000000000..9c74fb64d67b --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java @@ -0,0 +1,1222 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.languages; + +import lombok.Getter; +import lombok.Setter; +import org.openapitools.codegen.*; +import org.openapitools.codegen.meta.features.*; +import org.openapitools.codegen.model.ModelMap; +import org.openapitools.codegen.model.ModelsMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.commons.lang3.StringUtils; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.io.File; + +import static org.openapitools.codegen.utils.StringUtils.underscore; + +@SuppressWarnings("unchecked") +public class PostgresqlSchemaCodegen extends DefaultCodegen implements CodegenConfig { + private final Logger LOGGER = LoggerFactory.getLogger(PostgresqlSchemaCodegen.class); + + public static final String VENDOR_EXTENSION_POSTGRESQL_SCHEMA = "x-postgresql-schema"; + public static final String DEFAULT_DATABASE_NAME = "defaultDatabaseName"; + public static final String JSON_DATA_TYPE_ENABLED = "jsonDataTypeEnabled"; + public static final String IDENTIFIER_NAMING_CONVENTION = "identifierNamingConvention"; + public static final String NAMED_PARAMETERS_ENABLED = "namedParametersEnabled"; + public static final Integer ENUM_MAX_ELEMENTS = 65535; + public static final Integer IDENTIFIER_MAX_LENGTH = 64; + + protected Vector postgresqlNumericTypes = new Vector<>(Arrays.asList( + "BIGINT", "BIT", "BOOL", "BOOLEAN", "DEC", "DECIMAL", "DOUBLE", "DOUBLE PRECISION", "FIXED", "FLOAT", "INT", "INTEGER", "MEDIUMINT", "NUMERIC", "REAL", "SMALLINT", "TINYINT" + )); + + protected Vector postgresqlDateAndTimeTypes = new Vector<>(Arrays.asList( + "DATE", "DATETIME", "TIME", "TIMESTAMP", "YEAR" + )); + + protected Vector postgresqlStringTypes = new Vector<>(Arrays.asList( + "BINARY", "BLOB", "CHAR", "CHAR BYTE", "CHARACTER", "ENUM", "LONGBLOB", "LONGTEXT", "MEDIUMBLOB", "MEDIUMTEXT", "SET", "TEXT", "TINYBLOB", "TINYTEXT", "VARBINARY", "VARCHAR" + )); + + protected Vector postgresqlSpatialTypes = new Vector<>(Arrays.asList( + "GEOMETRY", "GEOMETRYCOLLECTION", "LINESTRING", "MULTILINESTRING", "MULTIPOINT", "MULTIPOLYGON", "POINT", "POLYGON" + )); + + /** + * Returns default database name for all PostgreSQL queries + * This value must be used with backticks only, e.g. `database_name` + */ + @Getter protected String defaultDatabaseName = "", databaseNamePrefix = "", databaseNameSuffix = "_db"; + protected String tableNamePrefix = "tbl_", tableNameSuffix = ""; + protected String columnNamePrefix = "col_", columnNameSuffix = ""; + /** + * Whether JSON data type enabled or disabled in all PostgreSQL queries. + * JSON data type requires PostgreSQL version 5.7.8 + */ + @Getter @Setter + protected Boolean jsonDataTypeEnabled = true; + /** + * Whether named parameters enabled or disabled in prepared SQLs + */ + @Getter @Setter + protected Boolean namedParametersEnabled = false; + /** + * Returns identifier naming convention for table names and column names. + */ + @Getter protected String identifierNamingConvention = "original"; + + public PostgresqlSchemaCodegen() { + super(); + + modifyFeatureSet(features -> features + .includeDocumentationFeatures(DocumentationFeature.Readme) + .wireFormatFeatures(EnumSet.noneOf(WireFormatFeature.class)) + .securityFeatures(EnumSet.noneOf(SecurityFeature.class)) + .excludeGlobalFeatures( + GlobalFeature.XMLStructureDefinitions, + GlobalFeature.Callbacks, + GlobalFeature.LinkObjects, + GlobalFeature.ParameterStyling + ) + .excludeSchemaSupportFeatures( + SchemaSupportFeature.Polymorphism + ) + .clientModificationFeatures(EnumSet.noneOf(ClientModificationFeature.class)) + ); + // clear import mapping (from default generator) as postgresql does not use import directives + importMapping.clear(); + + setModelPackage("Model"); + modelTemplateFiles.put("sql_query.mustache", ".sql"); + //modelTestTemplateFiles.put("model_test.mustache", ".php"); + // no doc files + // modelDocTemplateFiles.clear(); + // apiDocTemplateFiles.clear(); + + // https://dev.postgresql.com/doc/refman/8.0/en/keywords.html + setReservedWordsLowerCase( + Arrays.asList( + // SQL reserved words + "ACCESSIBLE", "ADD", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ASENSITIVE", + "BEFORE", "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOTH", "BY", + "CALL", "CASCADE", "CASE", "CHANGE", "CHAR", "CHARACTER", "CHECK", "COLLATE", "COLUMN", "CONDITION", "CONSTRAINT", "CONTINUE", "CONVERT", "CREATE", "CROSS", "CUBE", "CUME_DIST", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", + "DATABASE", "DATABASES", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", "DAY_SECOND", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DELAYED", "DELETE", "DENSE_RANK", "DESC", "DESCRIBE", "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DOUBLE", "DROP", "DUAL", + "EACH", "ELSE", "ELSEIF", "EMPTY", "ENCLOSED", "ESCAPED", "EXCEPT", "EXISTS", "EXIT", "EXPLAIN", + "FALSE", "FETCH", "FIRST_VALUE", "FLOAT", "FLOAT4", "FLOAT8", "FOR", "FORCE", "FOREIGN", "FROM", "FULLTEXT", "FUNCTION", + "GENERATED", "GET", "GRANT", "GROUP", "GROUPING", "GROUPS", + "HAVING", "HIGH_PRIORITY", "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", + "IF", "IGNORE", "IN", "INDEX", "INFILE", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", "INT1", "INT2", "INT3", "INT4", "INT8", "INTEGER", "INTERVAL", "INTO", "IO_AFTER_GTIDS", "IO_BEFORE_GTIDS", "IS", "ITERATE", + "JOIN", "JSON_TABLE", + "KEY", "KEYS", "KILL", + "LAG", "LAST_VALUE", "LEAD", "LEADING", "LEAVE", "LEFT", "LIKE", "LIMIT", "LINEAR", "LINES", "LOAD", "LOCALTIME", "LOCALTIMESTAMP", "LOCK", "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", + "MASTER_BIND", "MASTER_SSL_VERIFY_SERVER_CERT", "MATCH", "MAXVALUE", "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", "MIDDLEINT", "MINUTE_MICROSECOND", "MINUTE_SECOND", "MOD", "MODIFIES", + "NATURAL", "NOT", "NO_WRITE_TO_BINLOG", "NTH_VALUE", "NTILE", "NULL", "NUMERIC", + "OF", "ON", "OPTIMIZE", "OPTIMIZER_COSTS", "OPTION", "OPTIONALLY", "OR", "ORDER", "OUT", "OUTER", "OUTFILE", "OVER", + "PARTITION", "PERCENT_RANK", "PERSIST", "PERSIST_ONLY", "PRECISION", "PRIMARY", "PROCEDURE", "PURGE", + "RANGE", "RANK", "READ", "READS", "READ_WRITE", "REAL", "RECURSIVE", "REFERENCES", "REGEXP", "RELEASE", "RENAME", "REPEAT", "REPLACE", "REQUIRE", "RESIGNAL", "RESTRICT", "RETURN", "REVOKE", "RIGHT", "RLIKE", "ROLE", "ROW", "ROWS", "ROW_NUMBER", + "SCHEMA", "SCHEMAS", "SECOND_MICROSECOND", "SELECT", "SENSITIVE", "SEPARATOR", "SET", "SHOW", "SIGNAL", "SMALLINT", "SPATIAL", "SPECIFIC", "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "SQL_BIG_RESULT", "SQL_CALC_FOUND_ROWS", "SQL_SMALL_RESULT", "SSL", "STARTING", "STORED", "STRAIGHT_JOIN", "SYSTEM", + "TABLE", "TERMINATED", "THEN", "TINYBLOB", "TINYINT", "TINYTEXT", "TO", "TRAILING", "TRIGGER", "TRUE", + "UNDO", "UNION", "UNIQUE", "UNLOCK", "UNSIGNED", "UPDATE", "USAGE", "USE", "USING", "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP", + "VALUES", "VARBINARY", "VARCHAR", "VARCHARACTER", "VARYING", "VIRTUAL", + "WHEN", "WHERE", "WHILE", "WINDOW", "WITH", "WRITE", + "XOR", + "YEAR_MONTH", + "ZEROFILL" + ) + ); + + // all types can be threaded as primitives except array, object and refs + languageSpecificPrimitives = new HashSet<>( + Arrays.asList( + "bool", + "boolean", + "int", + "integer", + "double", + "float", + "string", + "date", + "Date", + "DateTime", + "long", + "short", + "char", + "ByteArray", + "binary", + "file", + "UUID", + "URI", + "BigDecimal", + "mixed", + "number", + "void", + "byte" + ) + ); + + // https://dev.postgresql.com/doc/refman/8.0/en/data-types.html + typeMapping.put("array", "JSON"); + typeMapping.put("set", "JSON"); + typeMapping.put("map", "JSON"); + typeMapping.put("List", "JSON"); + typeMapping.put("boolean", "BOOL"); + typeMapping.put("string", "TEXT"); + typeMapping.put("int", "INT"); + typeMapping.put("byte", "TEXT"); + typeMapping.put("float", "DECIMAL"); + typeMapping.put("number", "DECIMAL"); + typeMapping.put("date", "DATE"); + typeMapping.put("Date", "DATETIME"); + typeMapping.put("DateTime", "DATETIME"); + typeMapping.put("long", "BIGINT"); + typeMapping.put("short", "SMALLINT"); + typeMapping.put("char", "TEXT"); + typeMapping.put("double", "DECIMAL"); + typeMapping.put("object", "JSON"); + typeMapping.put("integer", "INT"); + typeMapping.put("ByteArray", "MEDIUMBLOB"); + typeMapping.put("binary", "MEDIUMBLOB"); + typeMapping.put("file", "MEDIUMBLOB"); + typeMapping.put("UUID", "TEXT"); + typeMapping.put("URI", "TEXT"); + typeMapping.put("BigDecimal", "DECIMAL"); + + embeddedTemplateDir = templateDir = "postgresql-schema"; + + // it seems that cli options from DefaultCodegen are useless here + cliOptions.clear(); + addOption(DEFAULT_DATABASE_NAME, "Default database name for all PostgreSQL queries", defaultDatabaseName); + addSwitch(JSON_DATA_TYPE_ENABLED, "Use special JSON PostgreSQL data type for complex model properties. Requires PostgreSQL version 5.7.8. Generates TEXT data type when disabled", jsonDataTypeEnabled); + addSwitch(NAMED_PARAMETERS_ENABLED, "Generates model prepared SQLs with named parameters, eg. :petName. Question mark placeholder used when option disabled.", namedParametersEnabled); + + // we used to snake_case table/column names, let's add this option + CliOption identifierNamingOpt = new CliOption(IDENTIFIER_NAMING_CONVENTION, + "Naming convention of PostgreSQL identifiers(table names and column names). This is not related to database name which is defined by " + DEFAULT_DATABASE_NAME + " option"); + + identifierNamingOpt.addEnum("original", "Do not transform original names") + .addEnum("snake_case", "Use snake_case names") + .setDefault("original"); + + cliOptions.add(identifierNamingOpt); + } + + @Override + public CodegenType getTag() { + return CodegenType.SCHEMA; + } + + @Override + public String getName() { + return "postgresql-schema"; + } + + @Override + public String getHelp() { + return "Generates a PostgreSQL schema based on the model or schema defined in the OpenAPI specification (v2, v3)."; + } + + @Override + public void processOpts() { + super.processOpts(); + + if (additionalProperties.containsKey(DEFAULT_DATABASE_NAME)) { + if (additionalProperties.get(DEFAULT_DATABASE_NAME).equals("")) { + additionalProperties.remove(DEFAULT_DATABASE_NAME); + } else { + this.setDefaultDatabaseName((String) additionalProperties.get(DEFAULT_DATABASE_NAME)); + // default database name may be escaped, need to overwrite additional prop + additionalProperties.put(DEFAULT_DATABASE_NAME, getDefaultDatabaseName()); + } + } + + if (additionalProperties.containsKey(JSON_DATA_TYPE_ENABLED)) { + this.setJsonDataTypeEnabled(Boolean.valueOf(additionalProperties.get(JSON_DATA_TYPE_ENABLED).toString())); + } else { + additionalProperties.put(JSON_DATA_TYPE_ENABLED, getJsonDataTypeEnabled()); + } + + if (additionalProperties.containsKey(NAMED_PARAMETERS_ENABLED)) { + this.setNamedParametersEnabled(Boolean.valueOf(additionalProperties.get(NAMED_PARAMETERS_ENABLED).toString())); + } + + additionalProperties.put(NAMED_PARAMETERS_ENABLED, getNamedParametersEnabled()); + + if (additionalProperties.containsKey(IDENTIFIER_NAMING_CONVENTION)) { + this.setIdentifierNamingConvention((String) additionalProperties.get(IDENTIFIER_NAMING_CONVENTION)); + } + + // make model src path available in mustache template + additionalProperties.put("modelSrcPath", "./" + toSrcPath(modelPackage)); + + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("postgresql_schema.mustache", "", "postgresql_schema.sql")); + } + + @Override + public ModelsMap postProcessModels(ModelsMap objs) { + objs = super.postProcessModels(objs); + + for (ModelMap mo : objs.getModels()) { + CodegenModel model = mo.getModel(); + String modelName = model.getName(); + String tableName = this.toTableName(modelName); + String modelDescription = model.getDescription(); + Map modelVendorExtensions = model.getVendorExtensions(); + Map postgresqlSchema = new HashMap<>(); + Map tableDefinition = new HashMap<>(); + + if (this.getIdentifierNamingConvention().equals("snake_case") && !modelName.equals(tableName)) { + // add original name in table comment + String commentExtra = "Original model name - " + modelName + "."; + modelDescription = (modelDescription == null || modelDescription.isEmpty()) ? commentExtra : modelDescription + ". " + commentExtra; + } + + if (modelVendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { + // user already specified schema values + LOGGER.info("Found vendor extension in '{}' model, autogeneration skipped", modelName); + } else { + modelVendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); + postgresqlSchema.put("tableDefinition", tableDefinition); + tableDefinition.put("tblName", tableName); + tableDefinition.put("tblComment", modelDescription); + } + } + + return objs; + } + + @Override + public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + switch (property.getDataType().toUpperCase(Locale.ROOT)) { + case "BOOL": + processBooleanTypeProperty(model, property); + break; + case "TINYINT": + case "SMALLINT": + case "INT": + case "BIGINT": + processIntegerTypeProperty(model, property); + break; + case "DECIMAL": + processDecimalTypeProperty(model, property); + break; + case "MEDIUMBLOB": + case "TEXT": + processStringTypeProperty(model, property); + break; + case "DATE": + case "DATETIME": + processDateTypeProperty(model, property); + break; + case "JSON": + processJsonTypeProperty(model, property); + break; + default: + processUnknownTypeProperty(model, property); + } + } + + /** + * Processes each model's property mapped to integer type and adds related vendor extensions + * + * @param model model + * @param property model's property + */ + public void processIntegerTypeProperty(CodegenModel model, CodegenProperty property) { + Map vendorExtensions = property.getVendorExtensions(); + Map postgresqlSchema = new HashMap<>(); + Map columnDefinition = new HashMap<>(); + ArrayList columnDataTypeArguments = new ArrayList(); + String baseName = property.getBaseName(); + String colName = this.toColumnName(baseName); + String dataType = property.getDataType(); + String dataFormat = property.getDataFormat(); + String description = property.getDescription(); + String minimum = property.getMinimum(); + String maximum = property.getMaximum(); + boolean exclusiveMinimum = property.getExclusiveMinimum(); + boolean exclusiveMaximum = property.getIExclusiveMaximum(); + String defaultValue = property.getDefaultValue(); + Boolean required = property.getRequired(); + boolean unsigned = false; + Boolean isUuid = property.isUuid; + Boolean isEnum = property.isEnum; + + if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { + // user already specified schema values + LOGGER.info("Found vendor extension in '{}' property, autogeneration skipped", baseName); + return; + } + + if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { + // add original name in column comment + String commentExtra = "Original param name - " + baseName + "."; + description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + } + + vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); + postgresqlSchema.put("columnDefinition", columnDefinition); + columnDefinition.put("colName", colName); + + if (Boolean.TRUE.equals(isEnum)) { + Map allowableValues = property.getAllowableValues(); + List enumValues = (List) allowableValues.get("values"); + for (int i = 0; i < enumValues.size(); i++) { + if (i > ENUM_MAX_ELEMENTS - 1) { + LOGGER.warn( + "ENUM column can have maximum of {} distinct elements, following value will be skipped: {}", + ENUM_MAX_ELEMENTS, (String) enumValues.get(i)); + break; + } + String value = String.valueOf(enumValues.get(i)); + columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(value)); + } + columnDefinition.put("colDataType", "ENUM"); + columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); + } else { + if ("int64".equals(dataFormat)) { + columnDefinition.put("colDataType", "BIGINT"); + } else { + Long min = (minimum != null) ? Long.parseLong(minimum) : null; + Long max = (maximum != null) ? Long.parseLong(maximum) : null; + if (exclusiveMinimum && min != null) min += 1; + if (exclusiveMaximum && max != null) max -= 1; + if (min != null && min >= 0) { + unsigned = true; + } + columnDefinition.put("colUnsigned", unsigned); + columnDefinition.put("colDataType", getPostgresqlMatchedIntegerDataType(min, max, unsigned)); + } + } + + if (Boolean.TRUE.equals(required)) { + columnDefinition.put("colNotNull", true); + } else { + columnDefinition.put("colNotNull", false); + try { + columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + } catch (RuntimeException exception) { + LOGGER.warn( + "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", + baseName, model.getName()); + columnDefinition.put("colDefault", null); + } + } + + if (description != null) { + columnDefinition.put("colComment", description); + } + } + + /** + * Processes each model's property mapped to decimal type and adds related vendor extensions + * + * @param model model + * @param property model's property + */ + public void processDecimalTypeProperty(CodegenModel model, CodegenProperty property) { + Map vendorExtensions = property.getVendorExtensions(); + Map postgresqlSchema = new HashMap<>(); + Map columnDefinition = new HashMap<>(); + ArrayList columnDataTypeArguments = new ArrayList(); + String baseName = property.getBaseName(); + String colName = this.toColumnName(baseName); + String dataType = property.getDataType(); + String dataFormat = property.getDataFormat(); + String description = property.getDescription(); + String minimum = property.getMinimum(); + String maximum = property.getMaximum(); + boolean exclusiveMinimum = property.getExclusiveMinimum(); + boolean exclusiveMaximum = property.getIExclusiveMaximum(); + String defaultValue = property.getDefaultValue(); + Boolean required = property.getRequired(); + boolean unsigned = false; + Boolean isEnum = property.isEnum; + + if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { + // user already specified schema values + LOGGER.info("Found vendor extension in '{}' property, autogeneration skipped", baseName); + return; + } + + if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { + // add original name in column comment + String commentExtra = "Original param name - " + baseName + "."; + description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + } + + vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); + postgresqlSchema.put("columnDefinition", columnDefinition); + columnDefinition.put("colName", colName); + + if (Boolean.TRUE.equals(isEnum)) { + Map allowableValues = property.getAllowableValues(); + List enumValues = (List) allowableValues.get("values"); + for (int i = 0; i < enumValues.size(); i++) { + if (i > ENUM_MAX_ELEMENTS - 1) { + LOGGER.warn( + "ENUM column can have maximum of {} distinct elements, following value will be skipped: {}", + ENUM_MAX_ELEMENTS, (String) enumValues.get(i)); + break; + } + String value = String.valueOf(enumValues.get(i)); + columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(value)); + } + columnDefinition.put("colDataType", "ENUM"); + columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); + } else { + Float min = (minimum != null) ? Float.valueOf(minimum) : null; + Float max = (maximum != null) ? Float.valueOf(maximum) : null; + if (exclusiveMinimum && min != null) min += 1; + if (exclusiveMaximum && max != null) max -= 1; + if (min != null && min >= 0) { + unsigned = true; + } + columnDefinition.put("colDataType", "DECIMAL"); + columnDefinition.put("colUnsigned", unsigned); + columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); + columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(20)); + columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(9)); + } + + if (Boolean.TRUE.equals(required)) { + columnDefinition.put("colNotNull", true); + } else { + columnDefinition.put("colNotNull", false); + try { + columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + } catch (RuntimeException exception) { + LOGGER.warn( + "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", + baseName, model.getName()); + columnDefinition.put("colDefault", null); + } + } + + if (description != null) { + columnDefinition.put("colComment", description); + } + } + + /** + * Processes each model's property mapped to boolean type and adds related vendor extensions + * + * @param model model + * @param property model's property + */ + public void processBooleanTypeProperty(CodegenModel model, CodegenProperty property) { + Map vendorExtensions = property.getVendorExtensions(); + Map postgresqlSchema = new HashMap<>(); + Map columnDefinition = new HashMap<>(); + ArrayList columnDataTypeArguments = new ArrayList(); + String baseName = property.getBaseName(); + String colName = this.toColumnName(baseName); + String description = property.getDescription(); + String defaultValue = property.getDefaultValue(); + Boolean required = property.getRequired(); + + if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { + // user already specified schema values + LOGGER.info("Found vendor extension in '{}' property, autogeneration skipped", baseName); + return; + } + + if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { + // add original name in column comment + String commentExtra = "Original param name - " + baseName + "."; + description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + } + + vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); + postgresqlSchema.put("columnDefinition", columnDefinition); + columnDefinition.put("colName", colName); + columnDefinition.put("colDataType", "TINYINT"); + columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); + columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(1)); + + if (Boolean.TRUE.equals(required)) { + columnDefinition.put("colNotNull", true); + } else { + columnDefinition.put("colNotNull", false); + try { + columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + } catch (RuntimeException exception) { + LOGGER.warn( + "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", + baseName, model.getName()); + columnDefinition.put("colDefault", null); + } + } + + if (description != null) { + columnDefinition.put("colComment", description); + } + } + + /** + * Processes each model's property mapped to string type and adds related vendor extensions + * + * @param model model + * @param property model's property + */ + public void processStringTypeProperty(CodegenModel model, CodegenProperty property) { + Map vendorExtensions = property.getVendorExtensions(); + Map postgresqlSchema = new HashMap<>(); + Map columnDefinition = new HashMap<>(); + ArrayList columnDataTypeArguments = new ArrayList(); + String baseName = property.getBaseName(); + String colName = this.toColumnName(baseName); + String dataType = property.getDataType(); + String dataFormat = property.getDataFormat(); + String description = property.getDescription(); + Integer minLength = property.getMinLength(); + Integer maxLength = property.getMaxLength(); + String defaultValue = property.getDefaultValue(); + Boolean required = property.getRequired(); + Boolean isEnum = property.isEnum; + + if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { + // user already specified schema values + LOGGER.info("Found vendor extension in '{}' property, autogeneration skipped", baseName); + return; + } + + if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { + // add original name in column comment + String commentExtra = "Original param name - " + baseName + "."; + description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + } + + vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); + postgresqlSchema.put("columnDefinition", columnDefinition); + columnDefinition.put("colName", colName); + + if (Boolean.TRUE.equals(isEnum)) { + Map allowableValues = property.getAllowableValues(); + List enumValues = (List) allowableValues.get("values"); + columnDefinition.put("colDataType", "ENUM"); + columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); + for (int i = 0; i < enumValues.size(); i++) { + if (i > ENUM_MAX_ELEMENTS - 1) { + LOGGER.warn( + "ENUM column can have maximum of {} distinct elements, following value will be skipped: {}", + ENUM_MAX_ELEMENTS, (String) enumValues.get(i)); + break; + } + String value = String.valueOf(enumValues.get(i)); + columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(value)); + } + } else if (dataType.equals("MEDIUMBLOB")) { + columnDefinition.put("colDataType", "MEDIUMBLOB"); + } else { + String matchedStringType = getPostgresqlMatchedStringDataType(minLength, maxLength); + columnDefinition.put("colDataType", matchedStringType); + if (matchedStringType.equals("CHAR") || matchedStringType.equals("VARCHAR")) { + columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); + columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument((maxLength != null) ? maxLength : 255)); + } + } + + if (Boolean.TRUE.equals(required)) { + columnDefinition.put("colNotNull", true); + } else { + columnDefinition.put("colNotNull", false); + try { + columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + } catch (RuntimeException exception) { + LOGGER.warn( + "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", + baseName, model.getName()); + columnDefinition.put("colDefault", null); + } + } + + if (description != null) { + columnDefinition.put("colComment", description); + } + } + + /** + * Processes each model's property mapped to date type and adds related vendor extensions + * + * @param model model + * @param property model's property + */ + public void processDateTypeProperty(CodegenModel model, CodegenProperty property) { + Map vendorExtensions = property.getVendorExtensions(); + Map postgresqlSchema = new HashMap<>(); + Map columnDefinition = new HashMap<>(); + String baseName = property.getBaseName(); + String colName = this.toColumnName(baseName); + String dataType = property.getDataType(); + Boolean required = property.getRequired(); + String description = property.getDescription(); + String defaultValue = property.getDefaultValue(); + + if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { + // user already specified schema values + LOGGER.info("Found vendor extension in '{}' property, autogeneration skipped", baseName); + return; + } + + if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { + // add original name in column comment + String commentExtra = "Original param name - " + baseName + "."; + description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + } + + vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); + postgresqlSchema.put("columnDefinition", columnDefinition); + columnDefinition.put("colName", colName); + columnDefinition.put("colDataType", dataType); + + if (Boolean.TRUE.equals(required)) { + columnDefinition.put("colNotNull", true); + } else { + columnDefinition.put("colNotNull", false); + try { + columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + } catch (RuntimeException exception) { + LOGGER.warn( + "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", + baseName, model.getName()); + columnDefinition.put("colDefault", null); + } + } + + if (description != null) { + columnDefinition.put("colComment", description); + } + } + + /** + * Processes each model's property mapped to JSON type and adds related vendor extensions + * + * @param model model + * @param property model's property + */ + public void processJsonTypeProperty(CodegenModel model, CodegenProperty property) { + Map vendorExtensions = property.getVendorExtensions(); + Map postgresqlSchema = new HashMap<>(); + Map columnDefinition = new HashMap<>(); + String baseName = property.getBaseName(); + String colName = this.toColumnName(baseName); + String dataType = property.getDataType(); + Boolean required = property.getRequired(); + String description = property.getDescription(); + String defaultValue = property.getDefaultValue(); + + if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { + // user already specified schema values + LOGGER.info("Found vendor extension in '{}' property, autogeneration skipped", baseName); + return; + } + + if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { + // add original name in column comment + String commentExtra = "Original param name - " + baseName + "."; + description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + } + + vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); + postgresqlSchema.put("columnDefinition", columnDefinition); + columnDefinition.put("colName", colName); + columnDefinition.put("colDataType", dataType); + if (Boolean.FALSE.equals(getJsonDataTypeEnabled())) { + columnDefinition.put("colDataType", "TEXT"); + } + + if (Boolean.TRUE.equals(required)) { + columnDefinition.put("colNotNull", true); + } else { + columnDefinition.put("colNotNull", false); + try { + columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + } catch (RuntimeException exception) { + LOGGER.warn( + "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", + baseName, model.getName()); + columnDefinition.put("colDefault", null); + } + } + + if (description != null) { + columnDefinition.put("colComment", description); + } + } + + /** + * Processes each model's property not mapped to any type and adds related vendor extensions + * Most of time it's related to referenced properties eg. \Model\User + * + * @param model model + * @param property model's property + */ + public void processUnknownTypeProperty(CodegenModel model, CodegenProperty property) { + Map vendorExtensions = property.getVendorExtensions(); + Map postgresqlSchema = new HashMap<>(); + Map columnDefinition = new HashMap<>(); + String baseName = property.getBaseName(); + String colName = this.toColumnName(baseName); + Boolean required = property.getRequired(); + String description = property.getDescription(); + String defaultValue = property.getDefaultValue(); + + if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { + // user already specified schema values + LOGGER.info("Found vendor extension in '{}' property, autogeneration skipped", baseName); + return; + } + + if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { + // add original name in column comment + String commentExtra = "Original param name - " + baseName + "."; + description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + } + + vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); + postgresqlSchema.put("columnDefinition", columnDefinition); + columnDefinition.put("colName", colName); + columnDefinition.put("colDataType", "TEXT"); + + if (Boolean.TRUE.equals(required)) { + columnDefinition.put("colNotNull", true); + } else { + columnDefinition.put("colNotNull", false); + try { + columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + } catch (RuntimeException exception) { + LOGGER.warn( + "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", + baseName, model.getName()); + columnDefinition.put("colDefault", null); + } + } + + if (description != null) { + columnDefinition.put("colComment", description); + } + } + + /** + * Generates codegen property for PostgreSQL data type argument + * + * @param value argument value + * @return generated codegen property + */ + public HashMap toCodegenPostgresqlDataTypeArgument(Object value) { + HashMap arg = new HashMap<>(); + if (value instanceof String) { + arg.put("isString", true); + arg.put("isFloat", false); + arg.put("isInteger", false); + arg.put("isNumeric", false); + } else if (value instanceof Integer || value instanceof Long) { + arg.put("isString", false); + arg.put("isFloat", false); + arg.put("isInteger", true); + arg.put("isNumeric", true); + } else if (value instanceof Number) { + arg.put("isString", false); + arg.put("isFloat", true); + arg.put("isInteger", false); + arg.put("isNumeric", true); + } else { + LOGGER.warn("PostgreSQL data type argument can be primitive type only. Class '{}' is provided", value.getClass()); + } + arg.put("argumentValue", value); + return arg; + } + + /** + * Generates default codegen property for PostgreSQL column definition + * Ref: https://dev.postgresql.com/doc/refman/5.7/en/data-type-defaults.html + * + * @param defaultValue value + * @param postgresqlDataType PostgreSQL data type + * @return generated codegen property + */ + public HashMap toCodegenPostgresqlDataTypeDefault(String defaultValue, String postgresqlDataType) { + HashMap defaultMap = new HashMap<>(); + if (defaultValue == null || defaultValue.toUpperCase(Locale.ROOT).equals("NULL")) { + defaultMap.put("defaultValue", "NULL"); + defaultMap.put("isString", false); + defaultMap.put("isNumeric", false); + defaultMap.put("isKeyword", true); + return defaultMap; + } + + switch (postgresqlDataType.toUpperCase(Locale.ROOT)) { + case "TINYINT": + case "SMALLINT": + case "MEDIUMINT": + case "INT": + case "BIGINT": + // SERIAL DEFAULT VALUE is a special case. In the definition of an integer column, it is an alias for NOT NULL AUTO_INCREMENT UNIQUE + if (defaultValue.equals("SERIAL DEFAULT VALUE")) { + defaultMap.put("defaultValue", defaultValue); + defaultMap.put("isString", false); + defaultMap.put("isNumeric", false); + defaultMap.put("isKeyword", true); + } else { + defaultMap.put("defaultValue", defaultValue); + defaultMap.put("isString", false); + defaultMap.put("isNumeric", true); + defaultMap.put("isKeyword", false); + } + return defaultMap; + case "TIMESTAMP": + case "DATETIME": + // The exception is that, for TIMESTAMP and DATETIME columns, you can specify CURRENT_TIMESTAMP as the default + if (defaultValue.equals("CURRENT_TIMESTAMP")) { + defaultMap.put("defaultValue", defaultValue); + defaultMap.put("isString", false); + defaultMap.put("isNumeric", false); + defaultMap.put("isKeyword", true); + } else { + defaultMap.put("defaultValue", defaultValue); + defaultMap.put("isString", true); + defaultMap.put("isNumeric", false); + defaultMap.put("isKeyword", false); + } + return defaultMap; + case "TINYBLOB": + case "BLOB": + case "MEDIUMBLOB": + case "LONGBLOB": + case "TINYTEXT": + case "TEXT": + case "MEDIUMTEXT": + case "LONGTEXT": + case "GEOMETRY": + case "JSON": + // The BLOB, TEXT, GEOMETRY, and JSON data types cannot be assigned a default value. + throw new RuntimeException("The BLOB, TEXT, GEOMETRY, and JSON data types cannot be assigned a default value"); + default: + defaultMap.put("defaultValue", defaultValue); + defaultMap.put("isString", true); + defaultMap.put("isNumeric", false); + defaultMap.put("isKeyword", false); + return defaultMap; + } + } + + /** + * Finds best fitted PostgreSQL data type for integer variable based on minimum and maximum properties + * + * @param minimum (optional) codegen property + * @param maximum (optional) codegen property + * @param unsigned (optional) whether variable is unsigned or not + * @return PostgreSQL integer data type + */ + public String getPostgresqlMatchedIntegerDataType(Long minimum, Long maximum, Boolean unsigned) { + // we can choose fit postgresql data type + // ref: https://dev.postgresql.com/doc/refman/8.0/en/integer-types.html + long min = (minimum != null) ? minimum : -2147483648L; + long max = (maximum != null) ? maximum : 2147483647L; + long actualMin = Math.min(min, max); // sometimes min and max values can be mixed up + long actualMax = Math.max(min, max); // sometimes only minimum specified and it can be pretty high + if (minimum != null && maximum != null && minimum > maximum) { + LOGGER.warn("Codegen property 'minimum' cannot be greater than 'maximum'"); + } + if (Boolean.TRUE.equals(unsigned) && actualMin >= 0) { + if (actualMax <= 255) { + return "TINYINT"; + } else if (actualMax <= 65535) { + return "SMALLINT"; + } else if (actualMax <= 16777215) { + return "MEDIUMINT"; + } else if (actualMax <= 4294967295L) { + return "INT"; + } else if (actualMax > 4294967295L) { + return "BIGINT"; + } + } else { + if (actualMin >= -128 && actualMax <= 127) { + return "TINYINT"; + } else if (actualMin >= -32768 && actualMax <= 32767) { + return "SMALLINT"; + } else if (actualMin >= -8388608 && actualMax <= 8388607) { + return "MEDIUMINT"; + } else if (actualMin >= -2147483648 && actualMax <= 2147483647) { + return "INT"; + } else if (actualMin < -2147483648 || actualMax > 2147483647) { + return "BIGINT"; + } + } + + return "INT"; + } + + /** + * Finds best fitted PostgreSQL data type for string variable based on minLength and maxLength properties + * + * @param minLength (optional) codegen property + * @param maxLength (optional) codegen property + * @return PostgreSQL string data type + */ + public String getPostgresqlMatchedStringDataType(Integer minLength, Integer maxLength) { + // we can choose fit postgresql data type + // ref: https://dev.postgresql.com/doc/refman/8.0/en/string-type-overview.html + int min = (minLength != null && minLength >= 0) ? minLength : 0; + int max = (maxLength != null && maxLength >= 0) ? maxLength : 65535; + Integer actualMin = Math.min(min, max); // sometimes minLength and maxLength values can be mixed up + Integer actualMax = Math.max(min, max); // sometimes only minLength specified and it can be pretty high + if (minLength != null && maxLength != null && minLength > maxLength) { + LOGGER.warn("Codegen property 'minLength' cannot be greater than 'maxLength'"); + } + if (actualMax.equals(actualMin) && actualMax <= 255) { + return "CHAR"; + } else if (actualMax <= 255) { + return "VARCHAR"; + } else if (actualMax > 255 && actualMax <= 65535) { + return "TEXT"; + } else if (actualMax > 65535 && actualMax <= 16777215) { + return "MEDIUMTEXT"; + } else if (actualMax > 16777215) { + return "LONGTEXT"; + } + return "TEXT"; + } + + /** + * Checks whether string is one of PostgreSQL Data Types + * Ref: https://dev.postgresql.com/doc/refman/8.0/en/data-type-overview.html + * + * @param dataType which needs to check + * @return true if value is correct PostgreSQL data type, otherwise false + */ + public Boolean isPostgresqlDataType(String dataType) { + return ( + postgresqlNumericTypes.contains(dataType.toUpperCase(Locale.ROOT)) || + postgresqlDateAndTimeTypes.contains(dataType.toUpperCase(Locale.ROOT)) || + postgresqlStringTypes.contains(dataType.toUpperCase(Locale.ROOT)) || + postgresqlSpatialTypes.contains(dataType.toUpperCase(Locale.ROOT)) || + dataType.toUpperCase(Locale.ROOT).equals("JSON") + ); + } + + /** + * Converts name to valid PostgreSQL database name + * Produced name must be used with backticks only, eg. `database_name` + * + * @param name source name + * @return database name + */ + public String toDatabaseName(String name) { + String identifier = toPostgresqlIdentifier(name, databaseNamePrefix, databaseNameSuffix); + if (identifier.length() > IDENTIFIER_MAX_LENGTH) { + LOGGER.warn("Database name cannot exceed 64 chars. Name '{}' will be truncated", name); + identifier = identifier.substring(0, IDENTIFIER_MAX_LENGTH); + } + return identifier; + } + + /** + * Converts name to valid PostgreSQL column name + * Produced name must be used with backticks only, eg. `table_name` + * + * @param name source name + * @return table name + */ + public String toTableName(String name) { + String identifier = toPostgresqlIdentifier(name, tableNamePrefix, tableNameSuffix); + if (identifierNamingConvention.equals("snake_case")) { + identifier = underscore(identifier); + } + if (identifier.length() > IDENTIFIER_MAX_LENGTH) { + LOGGER.warn("Table name cannot exceed 64 chars. Name '{}' will be truncated", name); + identifier = identifier.substring(0, IDENTIFIER_MAX_LENGTH); + } + return identifier; + } + + /** + * Converts name to valid PostgreSQL column name + * Produced name must be used with backticks only, eg. `column_name` + * + * @param name source name + * @return column name + */ + public String toColumnName(String name) { + String identifier = toPostgresqlIdentifier(name, columnNamePrefix, columnNameSuffix); + if (identifierNamingConvention.equals("snake_case")) { + identifier = underscore(identifier); + } + if (identifier.length() > IDENTIFIER_MAX_LENGTH) { + LOGGER.warn("Column name cannot exceed 64 chars. Name '{}' will be truncated", name); + identifier = identifier.substring(0, IDENTIFIER_MAX_LENGTH); + } + return identifier; + } + + /** + * Converts name to valid PostgreSQL identifier which can be used as database, table, column name + * Produced name must be used with backticks only, eg. `column_name` + * + * @param name source name + * @param prefix when escaped name is digits only, prefix will be prepended + * @param suffix when escaped name is digits only, suffix will be appended + * @return identifier name + */ + public String toPostgresqlIdentifier(String name, String prefix, String suffix) { + String escapedName = escapePostgresqlQuotedIdentifier(name); + // Database, table, and column names cannot end with space characters. + if (escapedName.matches(".*\\s$")) { + LOGGER.warn("Database, table, and column names cannot end with space characters. Check '{}' name", name); + escapedName = escapedName.replaceAll("\\s+$", ""); + } + + // Identifiers may begin with a digit but unless quoted may not consist solely of digits. + if (escapedName.matches("^\\d+$")) { + LOGGER.warn("Database, table, and column names cannot consist solely of digits. Check '{}' name", name); + escapedName = prefix + escapedName + suffix; + } + + // identifier name cannot be empty + if (escapedName.isEmpty()) { + throw new RuntimeException("Empty database/table/column name for property '" + name + "' not allowed"); + } + return escapedName; + } + + /** + * Escapes PostgreSQL identifier to use it in SQL statements without backticks, eg. SELECT identifier FROM + * Ref: https://dev.postgresql.com/doc/refman/8.0/en/identifiers.html + * + * @param identifier source identifier + * @return escaped identifier + */ + public String escapePostgresqlUnquotedIdentifier(String identifier) { + // ASCII: [0-9,a-z,A-Z$_] (basic Latin letters, digits 0-9, dollar, underscore) Extended: U+0080 .. U+FFFF + Pattern regexp = Pattern.compile("[^0-9a-zA-z$_\\u0080-\\uFFFF]"); + Matcher matcher = regexp.matcher(identifier); + if (matcher.find()) { + LOGGER.warn("Identifier '{}' contains unsafe characters out of [0-9,a-z,A-Z$_] and U+0080..U+FFFF range", + identifier); + identifier = identifier.replaceAll("[^0-9a-zA-z$_\\u0080-\\uFFFF]", ""); + } + + // ASCII NUL (U+0000) and supplementary characters (U+10000 and higher) are not permitted in quoted or unquoted identifiers. + // Don't know how to match these characters, hope that first regexp already strip them + // Pattern regexp2 = Pattern.compile("[\0\uD800\uDC00-\uDBFF\uDFFF]"); + return identifier; + } + + /** + * Escapes PostgreSQL identifier to use it in SQL statements with backticks, eg. SELECT `identifier` FROM + * Ref: https://dev.postgresql.com/doc/refman/8.0/en/identifiers.html + * + * @param identifier source identifier + * @return escaped identifier + */ + public String escapePostgresqlQuotedIdentifier(String identifier) { + // ASCII: U+0001 .. U+007F Extended: U+0080 .. U+FFFF + Pattern regexp = Pattern.compile("[^\\u0001-\\u007F\\u0080-\\uFFFF]"); + Matcher matcher = regexp.matcher(identifier); + if (matcher.find()) { + LOGGER.warn("Identifier '{}' contains unsafe characters out of U+0001..U+007F and U+0080..U+FFFF range", + identifier); + identifier = identifier.replaceAll("[^\\u0001-\\u007F\\u0080-\\uFFFF]", ""); + } + + // ASCII NUL (U+0000) and supplementary characters (U+10000 and higher) are not permitted in quoted or unquoted identifiers. + // Don't know how to match these characters, hope that first regexp already strip them + // Pattern regexp2 = Pattern.compile("[\0\uD800\uDC00-\uDBFF\uDFFF]"); + return identifier; + } + + @Override + public String escapeReservedWord(String name) { + LOGGER.warn( + "'{}' is PostgreSQL reserved word. Do not use that word or properly escape it with backticks in mustache template", + name); + return name; + } + + @Override + public String escapeQuotationMark(String input) { + // remove ' to avoid code injection + return input.replace("'", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("*/", "*_/").replace("/*", "/_*"); + } + + /** + * Sets default database name for all PostgreSQL queries + * Provided value will be escaped when necessary + * + * @param databaseName source name + */ + public void setDefaultDatabaseName(String databaseName) { + String escapedName = toDatabaseName(databaseName); + if (!escapedName.equals(databaseName)) { + LOGGER.error( + "Invalid database name. '{}' cannot be used as PostgreSQL identifier. Escaped value '{}' will be used instead.", + databaseName, escapedName); + } + this.defaultDatabaseName = escapedName; + } + + /** + * Sets identifier naming convention for table names and column names. + * This is not related to database name which is defined by defaultDatabaseName option. + * + * @param naming identifier naming convention (original|snake_case) + */ + public void setIdentifierNamingConvention(String naming) { + switch (naming) { + case "original": + case "snake_case": + this.identifierNamingConvention = naming; + break; + default: + LOGGER.warn("\"{}\" is invalid \"identifierNamingConvention\" argument. Current \"{}\" used instead.", + naming, this.identifierNamingConvention); + } + } + + /** + * Slightly modified version of AbstractPhpCodegen.toSrcPath method. + * + * @param packageName package name + * + * @return path + */ + public String toSrcPath(String packageName) { + // Trim prefix file separators from package path + String packagePath = StringUtils.removeStart( + // Replace period, backslash, forward slash with file separator in package name + packageName.replaceAll("[\\.\\\\/]", Matcher.quoteReplacement("/")), + File.separator + ); + + // Trim trailing file separators from the overall path + return StringUtils.removeEnd(packagePath, File.separator); + } + + @Override + public GeneratorLanguage generatorLanguage() { return GeneratorLanguage.POSTGRESQL; } +} diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index 636270f0a5ca..e5f7b479fb43 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -86,6 +86,7 @@ org.openapitools.codegen.languages.LuaClientCodegen org.openapitools.codegen.languages.MarkdownDocumentationCodegen org.openapitools.codegen.languages.JavaMicroprofileServerCodegen org.openapitools.codegen.languages.MysqlSchemaCodegen +org.openapitools.codegen.languages.PostgresqlSchemaCodegen org.openapitools.codegen.languages.N4jsClientCodegen org.openapitools.codegen.languages.NimClientCodegen org.openapitools.codegen.languages.NodeJSExpressServerCodegen diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/README.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/README.mustache new file mode 100644 index 000000000000..aee9538f4cd0 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/postgresql-schema/README.mustache @@ -0,0 +1,56 @@ +# PostgreSQL Schema Codegen + +Main goal of this generator is to provide database structure file almost identical you usually generate with: +- PHPMyAdmin (Export structure only, SQL syntax) +- Adminer +- `postgresqldump` function + +[PostgreSQL documentation](https://dev.postgresql.com/doc/) + +## Requirements +- PostgreSQL Server ^5.7.8 (`JSON` column type added) + +## Openapi Data Type to PostgreSQL Data Type mapping + +| Openapi Data Type | Openapi Data Format | Dependent properties | PostgreSQL Data Types | Default PostgreSQL Data Type | +| --- | --- | --- | --- | --- | +| `integer` | `int32` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `TINYINT` / `SMALLINT` / `MEDIUMINT`/ `INT` / `BIGINT` | `INT` | +| `integer` | `int64` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `TINYINT` / `SMALLINT` / `MEDIUMINT` / `INT` / `BIGINT` | `BIGINT` | +| `boolean` | | | `TINYINT` | `TINYINT` | +| `number` | `float` | | `DECIMAL` | `DECIMAL` | +| `number` | `double` | | `DECIMAL` | `DECIMAL` | +| `string` | | `minLength` / `maxLength` | `CHAR` / `VARCHAR` / `TEXT` / `MEDIUMTEXT` / `LONGTEXT` | `TEXT` | +| `string` | `byte` | | `TEXT` | `TEXT` | +| `string` | `binary` | | `MEDIUMBLOB` | `MEDIUMBLOB` | +| `file` | | | `MEDIUMBLOB` | `MEDIUMBLOB` | +| `string` | `date` | | `DATE` | `DATE` | +| `string` | `date-time` | | `DATETIME` | `DATETIME` | +| `string` | `enum` | | `ENUM` | `ENUM` | +| `array` | | | `JSON` | `JSON` | +| `object` | | | `JSON` | `JSON` | +| `\Model\User` (referenced definition) | | | `TEXT` | `TEXT` | + +## How to use + +Produced file(`postgresql_schema.sql`) contains every table definition. Current implementation doesn't drop or modify existed tables, if you want rewrite whole schema make sure they're not presented. + +### PHPMyAdmin + +1. Choose **Import** tab from the home screen +2. In section **File to import** click to **Choose File** and find generated `postgresql_schema.sql` +3. Make sure **Format** selector set to **SQL** +4. Push **Go** button + +### Adminer + +1. Click **Import** link in left sidebar +2. In **File upload** fieldset click to **Choose Files** and find generated `postgresql_schema.sql` +3. Push **Execute** button + +### Prepared SQL queries + +[Model folder]({{modelSrcPath}}) contains SQL queries(`SELECT *`, `SELECT`, `INSERT`, `UPDATE`, `DELETE`) usually suggested by `PHPMyAdmin` when you hit `SQL` tab. They are absolutely useless without adaptation to your needs. Copy-paste them then edit. + +Important! Some of SQLs(`INSERT`/`UPDATE`) contains {{#namedParametersEnabled}}named parameters eg. :namedParam{{/namedParametersEnabled}}{{^namedParametersEnabled}}question marks(`?`) which are parameter placeholders{{/namedParametersEnabled}}. You need to bind values to these params to execute query. + +If your PostgreSQL driver doesn't support named parameters(`PHP PDO` supports while `PHP postgresqli` doesn't) you need to make sure that `namedParametersEnabled` generator option is disabled. diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache new file mode 100644 index 000000000000..dfc67a3e81fb --- /dev/null +++ b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache @@ -0,0 +1,154 @@ +/* SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; */ +/* SET AUTOCOMMIT = 0; */ +/* START TRANSACTION; */ +/* SET time_zone = "+00:00"; */ +{{#defaultDatabaseName}} +-- +-- Database: `{{{defaultDatabaseName}}}` +-- +CREATE DATABASE IF NOT EXISTS `{{{defaultDatabaseName}}}` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +{{/defaultDatabaseName}} + +-- -------------------------------------------------------- + +{{#models}}{{#model}}{{#hasVars}}{{^isArray}}-- +-- Table structure{{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}} for table `{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} generated from model '{{classVarName}}' +{{#description}} +-- {{.}} +{{/description}} +-- + +{{#vendorExtensions}} +{{#x-postgresql-schema}} +{{#tableDefinition}} +CREATE TABLE IF NOT EXISTS {{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}`{{tblName}}` ( +{{/tableDefinition}} +{{/x-postgresql-schema}} +{{/vendorExtensions}} + {{#vars}} + {{#vendorExtensions}} + {{#x-postgresql-schema}} + {{#columnDefinition}} + `{{colName}}` {{colDataType}}{{#colDataTypeArguments}}{{#-first}}({{/-first}}{{#isString}}'{{/isString}}{{argumentValue}}{{#isString}}'{{/isString}}{{^-last}}, {{/-last}}{{#-last}}){{/-last}}{{/colDataTypeArguments}}{{#colUnsigned}} UNSIGNED{{/colUnsigned}}{{#colNotNull}} NOT NULL{{/colNotNull}}{{#colDefault}} DEFAULT {{#isString}}'{{defaultValue}}'{{/isString}}{{^isString}}{{defaultValue}}{{/isString}}{{/colDefault}}{{#colComment}} COMMENT '{{.}}'{{/colComment}}{{^-last}},{{/-last}} + {{/columnDefinition}} + {{/x-postgresql-schema}} + {{/vendorExtensions}} + {{/vars}} +{{#vendorExtensions}} +{{#x-postgresql-schema}} +{{#tableDefinition}} +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci{{#tblComment}} COMMENT='{{.}}'{{/tblComment}}; +{{/tableDefinition}} +{{/x-postgresql-schema}} +{{/vendorExtensions}} + +{{/isArray}}{{/hasVars}}{{/model}}{{/models}} +{{#hasOAuthMethods}} +-- +-- OAuth2 framework tables +-- Thanks to https://github.com/dsquier/oauth2-server-php-postgresql repo +-- + +-- +-- Table structure for table `oauth_clients` +-- +CREATE TABLE IF NOT EXISTS `oauth_clients` ( + `client_id` VARCHAR(80) NOT NULL, + `client_secret` VARCHAR(80) DEFAULT NULL, + `redirect_uri` VARCHAR(2000) DEFAULT NULL, + `grant_types` VARCHAR(80) DEFAULT NULL, + `scope` VARCHAR(4000) DEFAULT NULL, + `user_id` VARCHAR(80) DEFAULT NULL, + PRIMARY KEY (`client_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_access_tokens` +-- +CREATE TABLE IF NOT EXISTS `oauth_access_tokens` ( + `access_token` VARCHAR(40) NOT NULL, + `client_id` VARCHAR(80) DEFAULT NULL, + `user_id` VARCHAR(80) DEFAULT NULL, + `expires` TIMESTAMP NOT NULL, + `scope` VARCHAR(4000) DEFAULT NULL, + PRIMARY KEY (`access_token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_authorization_codes` +-- +CREATE TABLE IF NOT EXISTS `oauth_authorization_codes` ( + `authorization_code` VARCHAR(40) NOT NULL, + `client_id` VARCHAR(80) DEFAULT NULL, + `user_id` VARCHAR(80) DEFAULT NULL, + `redirect_uri` VARCHAR(2000) NOT NULL, + `expires` TIMESTAMP NOT NULL, + `scope` VARCHAR(4000) DEFAULT NULL, + `id_token` VARCHAR(1000) DEFAULT NULL, + PRIMARY KEY (`authorization_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_refresh_tokens` +-- +CREATE TABLE IF NOT EXISTS `oauth_refresh_tokens` ( + `refresh_token` VARCHAR(40) NOT NULL, + `client_id` VARCHAR(80) DEFAULT NULL, + `user_id` VARCHAR(80) DEFAULT NULL, + `expires` TIMESTAMP on update CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `scope` VARCHAR(4000) DEFAULT NULL, + PRIMARY KEY (`refresh_token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_users` +-- +CREATE TABLE IF NOT EXISTS `oauth_users` ( + `username` VARCHAR(80) DEFAULT NULL, + `password` VARCHAR(255) DEFAULT NULL, + `first_name` VARCHAR(80) DEFAULT NULL, + `last_name` VARCHAR(80) DEFAULT NULL, + `email` VARCHAR(2000) DEFAULT NULL, + `email_verified` TINYINT(1) DEFAULT NULL, + `scope` VARCHAR(4000) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_scopes` +-- +CREATE TABLE IF NOT EXISTS `oauth_scopes` ( + `scope` VARCHAR(80) NOT NULL, + `is_default` TINYINT(1) DEFAULT NULL, + PRIMARY KEY (`scope`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_jwt` +-- +CREATE TABLE IF NOT EXISTS `oauth_jwt` ( + `client_id` VARCHAR(80) NOT NULL, + `subject` VARCHAR(80) DEFAULT NULL, + `public_key` VARCHAR(2000) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_jti` +-- +CREATE TABLE IF NOT EXISTS `oauth_jti` ( + `issuer` VARCHAR(80) NOT NULL, + `subject` VARCHAR(80) DEFAULT NULL, + `audience` VARCHAR(80) DEFAULT NULL, + `expires` TIMESTAMP NOT NULL, + `jti` VARCHAR(2000) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_public_keys` +-- +CREATE TABLE IF NOT EXISTS `oauth_public_keys` ( + `client_id` VARCHAR(80) DEFAULT NULL, + `public_key` VARCHAR(2000) DEFAULT NULL, + `private_key` VARCHAR(2000) DEFAULT NULL, + `encryption_algorithm` VARCHAR(100) DEFAULT 'RS256' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +{{/hasOAuthMethods}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/sql_query.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/sql_query.mustache new file mode 100644 index 000000000000..054b0baecfc3 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/postgresql-schema/sql_query.mustache @@ -0,0 +1,27 @@ +-- +-- {{appName}}.{{#defaultDatabaseName}} +-- Database: `{{{.}}}`{{/defaultDatabaseName}} +-- Prepared SQL queries for {{#models}}{{#model}}'{{{name}}}'{{/model}}{{/models}} definition. +-- + +{{#models}}{{#model}} +-- +-- SELECT template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} +-- +SELECT {{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}`{{colName}}`{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}} FROM {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} WHERE 1; + +-- +-- INSERT template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} +-- +INSERT INTO {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}({{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}`{{colName}}`{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}}) VALUES ({{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}{{#namedParametersEnabled}}:{{colName}}{{/namedParametersEnabled}}{{^namedParametersEnabled}}?{{/namedParametersEnabled}}{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}}); + +-- +-- UPDATE template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} +-- +UPDATE {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} SET {{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}`{{colName}}` = {{#namedParametersEnabled}}:{{colName}}{{/namedParametersEnabled}}{{^namedParametersEnabled}}?{{/namedParametersEnabled}}{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}} WHERE 1; + +-- +-- DELETE template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} +-- +DELETE FROM {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} WHERE 0; +{{/model}}{{/models}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/PostgresqlSchemaOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/PostgresqlSchemaOptionsProvider.java new file mode 100644 index 000000000000..7e71c334342c --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/PostgresqlSchemaOptionsProvider.java @@ -0,0 +1,49 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.options; + +import com.google.common.collect.ImmutableMap; +import org.openapitools.codegen.languages.PostgresqlSchemaCodegen; + +import java.util.Map; + +public class PostgresqlSchemaOptionsProvider implements OptionsProvider { + public static final String DEFAULT_DATABASE_NAME_VALUE = "database_name"; + public static final String JSON_DATA_TYPE_ENABLED_VALUE = "false"; + public static final String IDENTIFIER_NAMING_CONVENTION_VALUE = "snake_case"; + public static final String NAMED_PARAMETERS_ENABLED_VALUE = "true"; + + @Override + public String getLanguage() { + return "postgresql-schema"; + } + + @Override + public Map createOptions() { + ImmutableMap.Builder builder = new ImmutableMap.Builder(); + return builder.put(PostgresqlSchemaCodegen.DEFAULT_DATABASE_NAME, DEFAULT_DATABASE_NAME_VALUE) + .put(PostgresqlSchemaCodegen.JSON_DATA_TYPE_ENABLED, JSON_DATA_TYPE_ENABLED_VALUE) + .put(PostgresqlSchemaCodegen.IDENTIFIER_NAMING_CONVENTION, IDENTIFIER_NAMING_CONVENTION_VALUE) + .put(PostgresqlSchemaCodegen.NAMED_PARAMETERS_ENABLED, NAMED_PARAMETERS_ENABLED_VALUE) + .build(); + } + + @Override + public boolean isServer() { + return false; + } +} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java new file mode 100644 index 000000000000..857efee5ac71 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java @@ -0,0 +1,311 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.postgresql; + +import org.openapitools.codegen.languages.PostgresqlSchemaCodegen; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Set; + +public class PostgresqlSchemaCodegenTest { + + @Test + public void testGetPostgresqlMatchedIntegerDataType() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(null, null, null), "INT"); + + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-128L, 127L, false), "TINYINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 255L, true), "TINYINT"); + + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(500L, 100L, null), "SMALLINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(500L, 100L, true), "SMALLINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(500L, 100L, false), "SMALLINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-32768L, 32767L, false), "SMALLINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 65535L, true), "SMALLINT"); + + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-8388608L, 8388607L, false), "MEDIUMINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 16777215L, true), "MEDIUMINT"); + + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-2147483648L, 2147483647L, false), "INT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(Long.parseLong(String.valueOf(Integer.MIN_VALUE)), Long.parseLong(String.valueOf(Integer.MAX_VALUE)), false), "INT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 4294967295L, true), "INT"); + + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-2147483649L, 2147483648L, false), "BIGINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 4294967296L, true), "BIGINT"); + } + + @Test + public void testGetPostgresqlMatchedStringDataType() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(6, 6), "CHAR"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(0, 0), "CHAR"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(255, 255), "CHAR"); + + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 100), "VARCHAR"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 255), "VARCHAR"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(50, 255), "VARCHAR"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, 20), "VARCHAR"); + + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, null), "TEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, null), "TEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(255, null), "TEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 256), "TEXT"); + + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777215, null), "MEDIUMTEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777215, 100), "MEDIUMTEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 16777215), "MEDIUMTEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, 16777215), "MEDIUMTEXT"); + + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777216, null), "LONGTEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 16777216), "LONGTEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777216, 16777216), "LONGTEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, 16777216), "LONGTEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, Integer.MAX_VALUE), "LONGTEXT"); + } + + @Test + public void testToCodegenPostgresqlDataTypeArgument() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + String strArgument = "HelloWorld"; + HashMap strProp = codegen.toCodegenPostgresqlDataTypeArgument(strArgument); + Assert.assertTrue((Boolean) strProp.get("isString")); + Assert.assertFalse((Boolean) strProp.get("isFloat")); + Assert.assertFalse((Boolean) strProp.get("isInteger")); + Assert.assertFalse((Boolean) strProp.get("isNumeric")); + Assert.assertSame(strProp.get("argumentValue"), strArgument); + + Integer intArgument = 10; + HashMap intProp = codegen.toCodegenPostgresqlDataTypeArgument(intArgument); + Assert.assertFalse((Boolean) intProp.get("isString")); + Assert.assertFalse((Boolean) intProp.get("isFloat")); + Assert.assertTrue((Boolean) intProp.get("isInteger")); + Assert.assertTrue((Boolean) intProp.get("isNumeric")); + Assert.assertSame(intProp.get("argumentValue"), intArgument); + + Double floatArgument = 3.14; + HashMap floatProp = codegen.toCodegenPostgresqlDataTypeArgument(floatArgument); + Assert.assertFalse((Boolean) floatProp.get("isString")); + Assert.assertTrue((Boolean) floatProp.get("isFloat")); + Assert.assertFalse((Boolean) floatProp.get("isInteger")); + Assert.assertTrue((Boolean) floatProp.get("isNumeric")); + Assert.assertSame(floatProp.get("argumentValue"), floatArgument); + } + + @Test + public void testToCodegenPostgresqlDataTypeDefault() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + HashMap defaultMap = null; + ArrayList intFixture = new ArrayList(Arrays.asList( + "TINYINT", "SmallInt", "Mediumint", "INT", "bigint" + )); + for(String intType : intFixture) { + defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("150", intType); + Assert.assertTrue((Boolean) defaultMap.get("isNumeric")); + Assert.assertFalse((Boolean) defaultMap.get("isString")); + Assert.assertFalse((Boolean) defaultMap.get("isKeyword")); + Assert.assertSame(defaultMap.get("defaultValue"), "150"); + } + defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("SERIAL DEFAULT VALUE", "TINYINT"); + Assert.assertFalse((Boolean) defaultMap.get("isNumeric")); + Assert.assertFalse((Boolean) defaultMap.get("isString")); + Assert.assertTrue((Boolean) defaultMap.get("isKeyword")); + Assert.assertSame(defaultMap.get("defaultValue"), "SERIAL DEFAULT VALUE"); + + ArrayList dateFixture = new ArrayList(Arrays.asList( + "Timestamp", "DateTime" + )); + for(String dateType : dateFixture) { + defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("2018-08-12", dateType); + Assert.assertFalse((Boolean) defaultMap.get("isNumeric")); + Assert.assertTrue((Boolean) defaultMap.get("isString")); + Assert.assertFalse((Boolean) defaultMap.get("isKeyword")); + Assert.assertSame(defaultMap.get("defaultValue"), "2018-08-12"); + } + defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("CURRENT_TIMESTAMP", "Timestamp"); + Assert.assertFalse((Boolean) defaultMap.get("isNumeric")); + Assert.assertFalse((Boolean) defaultMap.get("isString")); + Assert.assertTrue((Boolean) defaultMap.get("isKeyword")); + Assert.assertSame(defaultMap.get("defaultValue"), "CURRENT_TIMESTAMP"); + + ArrayList restFixture = new ArrayList(Arrays.asList( + "VARCHAR", "CHAR", "ENUM", "UNKNOWN" + )); + for(String restType : restFixture) { + defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("sometext", restType); + Assert.assertFalse((Boolean) defaultMap.get("isNumeric")); + Assert.assertTrue((Boolean) defaultMap.get("isString")); + Assert.assertFalse((Boolean) defaultMap.get("isKeyword")); + Assert.assertSame(defaultMap.get("defaultValue"), "sometext"); + } + } + + @Test(expectedExceptions = RuntimeException.class) + public void testToCodegenPostgresqlDataTypeDefaultWithExceptionalColumnType() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + HashMap defaultMap = null; + ArrayList specialFixture = new ArrayList(Arrays.asList( + "TINYBLOB", "Blob", "MEDIUMBLOB", "LONGBLOB", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "GEOMETRY", "JSON" + )); + for(String specialType : specialFixture) { + defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("2018-08-12", specialType); + Assert.assertNull(defaultMap); + } + } + + @Test + public void testIsPostgresqlDataType() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + ArrayList trueFixture = new ArrayList(Arrays.asList( + "INTEGER", "integer", "Integer", "DATETIME", "datetime", "DateTime", "VARCHAR", "varchar", "VarChar", "POINT", "Point", "point", "JSON", "json", "Json" + )); + ArrayList falseFixture = new ArrayList(Arrays.asList( + "unknown", "HashMap", "HASHMAP", "hashmap" + )); + for(String trueValue : trueFixture) { + Assert.assertTrue(codegen.isPostgresqlDataType(trueValue), "'" + trueValue + "' isn't PostgreSQL data type"); + } + for(String falseValue : falseFixture) { + Assert.assertFalse(codegen.isPostgresqlDataType(falseValue), "'" + falseValue + "' is PostgreSQL data type"); + } + } + + @Test + public void testToPostgresqlIdentifier() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertEquals(codegen.toPostgresqlIdentifier("table_name", "tbl_", ""), "table_name"); + Assert.assertEquals(codegen.toPostgresqlIdentifier("table_name ", "tbl_", ""), "table_name"); + Assert.assertEquals(codegen.toPostgresqlIdentifier("12345678", "tbl_", ""), "tbl_12345678"); + } + + @Test(expectedExceptions = RuntimeException.class) + public void testToPostgresqlIdentifierWithEmptyString() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + codegen.toPostgresqlIdentifier(" ", "tbl_", ""); + } + + @Test + public void testEscapePostgresqlUnquotedIdentifier() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertEquals(codegen.escapePostgresqlUnquotedIdentifier("table1Z$_"), "table1Z$_"); + Assert.assertEquals(codegen.escapePostgresqlUnquotedIdentifier("table1Z$_!#%~&?()*+-./"), "table1Z$_"); + Assert.assertEquals(codegen.escapePostgresqlUnquotedIdentifier("table1Z$_русскийтекст"), "table1Z$_русскийтекст"); + Assert.assertEquals(codegen.escapePostgresqlQuotedIdentifier("table𐀀"), "table"); + Assert.assertEquals(codegen.escapePostgresqlQuotedIdentifier("table_name!'()�"), "table_name!'()�"); + Assert.assertEquals(codegen.escapePostgresqlQuotedIdentifier("table_name𐌅𐌌"), "table_name"); + } + + @Test + public void testEscapePostgresqlQuotedIdentifier() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertEquals(codegen.escapePostgresqlQuotedIdentifier("table"), "table"); + Assert.assertEquals(codegen.escapePostgresqlQuotedIdentifier("table𐀀"), "table"); + Assert.assertEquals(codegen.escapePostgresqlQuotedIdentifier("table_name!'()�"), "table_name!'()�"); + Assert.assertEquals(codegen.escapePostgresqlQuotedIdentifier("table_name𐌅𐌌"), "table_name"); + } + + @Test + public void testIsReservedWord() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Set reservedWords = codegen.reservedWords(); + ArrayList trueFixture = new ArrayList(Arrays.asList( + "accessible", "asc", "between", "blob", "change", "column", "day_hour", "distinct", "enclosed", "except", "explain", "float", "for", "function", "grant", "grouping", "high_priority", "groups", "hour_minute", "insensitive", "interval", "json_table", "keys", "kill", "leave", "left", "mediumblob", "modifies", "not", "null", "numeric", "optimize", "outer", "precision", "primary", "references", "replace", "select", "sql", "then", "tinytext", "unique", "unlock", "varchar", "virtual", "when", "where", "xor", "year_month", "zerofill" + )); + ArrayList falseFixture = new ArrayList(Arrays.asList( + "after", "boolean", "charset", "cpu", "current", "delay_key_write", "end", "format", "global", "host", "install", "json", "key_block_size", "local", "max_size", "none", "offset", "partial", "quarter", "relay", "second", "status", "timestamp", "until", "variables", "without", "xml", "year" + )); + for(String trueValue : trueFixture) { + Assert.assertTrue(reservedWords.contains(trueValue), "'" + trueValue + "' isn't PostgreSQL reserved word"); + } + for(String falseValue : falseFixture) { + Assert.assertFalse(reservedWords.contains(falseValue), "'" + falseValue + "' is PostgreSQL reserved word"); + } + } + + @Test + public void testSetDefaultDatabaseName() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + codegen.setDefaultDatabaseName("valid_db_name"); + Assert.assertSame(codegen.getDefaultDatabaseName(), "valid_db_name"); + codegen.setDefaultDatabaseName("12345"); + Assert.assertNotSame(codegen.getDefaultDatabaseName(), "12345"); + } + + @Test + public void testGetDefaultDatabaseName() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertSame(codegen.getDefaultDatabaseName(), ""); + } + + @Test + public void testSetJsonDataTypeEnabled() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + codegen.setJsonDataTypeEnabled(true); + Assert.assertTrue(codegen.getJsonDataTypeEnabled()); + codegen.setJsonDataTypeEnabled(false); + Assert.assertFalse(codegen.getJsonDataTypeEnabled()); + } + + @Test + public void testGetJsonDataTypeEnabled() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertTrue(codegen.getJsonDataTypeEnabled()); + codegen.setJsonDataTypeEnabled(false); + Assert.assertFalse(codegen.getJsonDataTypeEnabled()); + } + + @Test + public void testSetNamedParametersEnabled() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + codegen.setNamedParametersEnabled(true); + Assert.assertTrue(codegen.getNamedParametersEnabled()); + codegen.setNamedParametersEnabled(false); + Assert.assertFalse(codegen.getNamedParametersEnabled()); + } + + @Test + public void testGetNamedParametersEnabled() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertFalse(codegen.getNamedParametersEnabled()); + codegen.setNamedParametersEnabled(true); + Assert.assertTrue(codegen.getNamedParametersEnabled()); + } + + @Test + public void testSetIdentifierNamingConvention() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertSame("original", codegen.getIdentifierNamingConvention()); + codegen.setIdentifierNamingConvention("invalidValue"); + Assert.assertSame("original", codegen.getIdentifierNamingConvention()); + codegen.setIdentifierNamingConvention("snake_case"); + Assert.assertSame("snake_case", codegen.getIdentifierNamingConvention()); + codegen.setIdentifierNamingConvention("anotherInvalid"); + Assert.assertSame("snake_case", codegen.getIdentifierNamingConvention()); + } + + @Test + public void testGetIdentifierNamingConvention() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertSame("original", codegen.getIdentifierNamingConvention()); + codegen.setIdentifierNamingConvention("snake_case"); + Assert.assertSame("snake_case", codegen.getIdentifierNamingConvention()); + } + +} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java new file mode 100644 index 000000000000..ee02677ad970 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.postgresql; + +import org.openapitools.codegen.AbstractOptionsTest; +import org.openapitools.codegen.CodegenConfig; +import org.openapitools.codegen.languages.PostgresqlSchemaCodegen; +import org.openapitools.codegen.options.PostgresqlSchemaOptionsProvider; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class PostgresqlSchemaOptionsTest extends AbstractOptionsTest { + private PostgresqlSchemaCodegen clientCodegen = mock(PostgresqlSchemaCodegen.class, mockSettings); + + public PostgresqlSchemaOptionsTest() { + super(new PostgresqlSchemaOptionsProvider()); + } + + @Override + protected CodegenConfig getCodegenConfig() { + return clientCodegen; + } + + @SuppressWarnings("unused") + @Override + protected void verifyOptions() { + verify(clientCodegen).setDefaultDatabaseName(PostgresqlSchemaOptionsProvider.DEFAULT_DATABASE_NAME_VALUE); + verify(clientCodegen).setJsonDataTypeEnabled(Boolean.valueOf(PostgresqlSchemaOptionsProvider.JSON_DATA_TYPE_ENABLED_VALUE)); + verify(clientCodegen).setIdentifierNamingConvention(PostgresqlSchemaOptionsProvider.IDENTIFIER_NAMING_CONVENTION_VALUE); + verify(clientCodegen).setNamedParametersEnabled(Boolean.valueOf(PostgresqlSchemaOptionsProvider.NAMED_PARAMETERS_ENABLED_VALUE)); + } +} diff --git a/samples/schema/petstore/postgresql/.openapi-generator-ignore b/samples/schema/petstore/postgresql/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/schema/petstore/postgresql/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/schema/petstore/postgresql/.openapi-generator/FILES b/samples/schema/petstore/postgresql/.openapi-generator/FILES new file mode 100644 index 000000000000..61c5bf401036 --- /dev/null +++ b/samples/schema/petstore/postgresql/.openapi-generator/FILES @@ -0,0 +1,8 @@ +Model/ApiResponse.sql +Model/Category.sql +Model/Order.sql +Model/Pet.sql +Model/Tag.sql +Model/User.sql +README.md +postgresql_schema.sql diff --git a/samples/schema/petstore/postgresql/.openapi-generator/VERSION b/samples/schema/petstore/postgresql/.openapi-generator/VERSION new file mode 100644 index 000000000000..884119126398 --- /dev/null +++ b/samples/schema/petstore/postgresql/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.11.0-SNAPSHOT diff --git a/samples/schema/petstore/postgresql/Model/ApiResponse.sql b/samples/schema/petstore/postgresql/Model/ApiResponse.sql new file mode 100644 index 000000000000..8aac784b7f89 --- /dev/null +++ b/samples/schema/petstore/postgresql/Model/ApiResponse.sql @@ -0,0 +1,26 @@ +-- +-- OpenAPI Petstore. +-- Prepared SQL queries for 'ApiResponse' definition. +-- + + +-- +-- SELECT template for table `ApiResponse` +-- +SELECT `code`, `type`, `message` FROM `ApiResponse` WHERE 1; + +-- +-- INSERT template for table `ApiResponse` +-- +INSERT INTO `ApiResponse`(`code`, `type`, `message`) VALUES (?, ?, ?); + +-- +-- UPDATE template for table `ApiResponse` +-- +UPDATE `ApiResponse` SET `code` = ?, `type` = ?, `message` = ? WHERE 1; + +-- +-- DELETE template for table `ApiResponse` +-- +DELETE FROM `ApiResponse` WHERE 0; + diff --git a/samples/schema/petstore/postgresql/Model/Category.sql b/samples/schema/petstore/postgresql/Model/Category.sql new file mode 100644 index 000000000000..f7d7166644d2 --- /dev/null +++ b/samples/schema/petstore/postgresql/Model/Category.sql @@ -0,0 +1,26 @@ +-- +-- OpenAPI Petstore. +-- Prepared SQL queries for 'Category' definition. +-- + + +-- +-- SELECT template for table `Category` +-- +SELECT `id`, `name` FROM `Category` WHERE 1; + +-- +-- INSERT template for table `Category` +-- +INSERT INTO `Category`(`id`, `name`) VALUES (?, ?); + +-- +-- UPDATE template for table `Category` +-- +UPDATE `Category` SET `id` = ?, `name` = ? WHERE 1; + +-- +-- DELETE template for table `Category` +-- +DELETE FROM `Category` WHERE 0; + diff --git a/samples/schema/petstore/postgresql/Model/Order.sql b/samples/schema/petstore/postgresql/Model/Order.sql new file mode 100644 index 000000000000..43af471ffb5c --- /dev/null +++ b/samples/schema/petstore/postgresql/Model/Order.sql @@ -0,0 +1,26 @@ +-- +-- OpenAPI Petstore. +-- Prepared SQL queries for 'Order' definition. +-- + + +-- +-- SELECT template for table `Order` +-- +SELECT `id`, `petId`, `quantity`, `shipDate`, `status`, `complete` FROM `Order` WHERE 1; + +-- +-- INSERT template for table `Order` +-- +INSERT INTO `Order`(`id`, `petId`, `quantity`, `shipDate`, `status`, `complete`) VALUES (?, ?, ?, ?, ?, ?); + +-- +-- UPDATE template for table `Order` +-- +UPDATE `Order` SET `id` = ?, `petId` = ?, `quantity` = ?, `shipDate` = ?, `status` = ?, `complete` = ? WHERE 1; + +-- +-- DELETE template for table `Order` +-- +DELETE FROM `Order` WHERE 0; + diff --git a/samples/schema/petstore/postgresql/Model/Pet.sql b/samples/schema/petstore/postgresql/Model/Pet.sql new file mode 100644 index 000000000000..b4c6a135c038 --- /dev/null +++ b/samples/schema/petstore/postgresql/Model/Pet.sql @@ -0,0 +1,26 @@ +-- +-- OpenAPI Petstore. +-- Prepared SQL queries for 'Pet' definition. +-- + + +-- +-- SELECT template for table `Pet` +-- +SELECT `id`, `category`, `name`, `photoUrls`, `tags`, `status` FROM `Pet` WHERE 1; + +-- +-- INSERT template for table `Pet` +-- +INSERT INTO `Pet`(`id`, `category`, `name`, `photoUrls`, `tags`, `status`) VALUES (?, ?, ?, ?, ?, ?); + +-- +-- UPDATE template for table `Pet` +-- +UPDATE `Pet` SET `id` = ?, `category` = ?, `name` = ?, `photoUrls` = ?, `tags` = ?, `status` = ? WHERE 1; + +-- +-- DELETE template for table `Pet` +-- +DELETE FROM `Pet` WHERE 0; + diff --git a/samples/schema/petstore/postgresql/Model/Tag.sql b/samples/schema/petstore/postgresql/Model/Tag.sql new file mode 100644 index 000000000000..d62633f8427e --- /dev/null +++ b/samples/schema/petstore/postgresql/Model/Tag.sql @@ -0,0 +1,26 @@ +-- +-- OpenAPI Petstore. +-- Prepared SQL queries for 'Tag' definition. +-- + + +-- +-- SELECT template for table `Tag` +-- +SELECT `id`, `name` FROM `Tag` WHERE 1; + +-- +-- INSERT template for table `Tag` +-- +INSERT INTO `Tag`(`id`, `name`) VALUES (?, ?); + +-- +-- UPDATE template for table `Tag` +-- +UPDATE `Tag` SET `id` = ?, `name` = ? WHERE 1; + +-- +-- DELETE template for table `Tag` +-- +DELETE FROM `Tag` WHERE 0; + diff --git a/samples/schema/petstore/postgresql/Model/User.sql b/samples/schema/petstore/postgresql/Model/User.sql new file mode 100644 index 000000000000..91afbc9e2457 --- /dev/null +++ b/samples/schema/petstore/postgresql/Model/User.sql @@ -0,0 +1,26 @@ +-- +-- OpenAPI Petstore. +-- Prepared SQL queries for 'User' definition. +-- + + +-- +-- SELECT template for table `User` +-- +SELECT `id`, `username`, `firstName`, `lastName`, `email`, `password`, `phone`, `userStatus` FROM `User` WHERE 1; + +-- +-- INSERT template for table `User` +-- +INSERT INTO `User`(`id`, `username`, `firstName`, `lastName`, `email`, `password`, `phone`, `userStatus`) VALUES (?, ?, ?, ?, ?, ?, ?, ?); + +-- +-- UPDATE template for table `User` +-- +UPDATE `User` SET `id` = ?, `username` = ?, `firstName` = ?, `lastName` = ?, `email` = ?, `password` = ?, `phone` = ?, `userStatus` = ? WHERE 1; + +-- +-- DELETE template for table `User` +-- +DELETE FROM `User` WHERE 0; + diff --git a/samples/schema/petstore/postgresql/README.md b/samples/schema/petstore/postgresql/README.md new file mode 100644 index 000000000000..0e0d7d5887ff --- /dev/null +++ b/samples/schema/petstore/postgresql/README.md @@ -0,0 +1,56 @@ +# PostgreSQL Schema Codegen + +Main goal of this generator is to provide database structure file almost identical you usually generate with: +- PHPMyAdmin (Export structure only, SQL syntax) +- Adminer +- `postgresqldump` function + +[PostgreSQL documentation](https://dev.postgresql.com/doc/) + +## Requirements +- PostgreSQL Server ^5.7.8 (`JSON` column type added) + +## Openapi Data Type to PostgreSQL Data Type mapping + +| Openapi Data Type | Openapi Data Format | Dependent properties | PostgreSQL Data Types | Default PostgreSQL Data Type | +| --- | --- | --- | --- | --- | +| `integer` | `int32` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `TINYINT` / `SMALLINT` / `MEDIUMINT`/ `INT` / `BIGINT` | `INT` | +| `integer` | `int64` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `TINYINT` / `SMALLINT` / `MEDIUMINT` / `INT` / `BIGINT` | `BIGINT` | +| `boolean` | | | `TINYINT` | `TINYINT` | +| `number` | `float` | | `DECIMAL` | `DECIMAL` | +| `number` | `double` | | `DECIMAL` | `DECIMAL` | +| `string` | | `minLength` / `maxLength` | `CHAR` / `VARCHAR` / `TEXT` / `MEDIUMTEXT` / `LONGTEXT` | `TEXT` | +| `string` | `byte` | | `TEXT` | `TEXT` | +| `string` | `binary` | | `MEDIUMBLOB` | `MEDIUMBLOB` | +| `file` | | | `MEDIUMBLOB` | `MEDIUMBLOB` | +| `string` | `date` | | `DATE` | `DATE` | +| `string` | `date-time` | | `DATETIME` | `DATETIME` | +| `string` | `enum` | | `ENUM` | `ENUM` | +| `array` | | | `JSON` | `JSON` | +| `object` | | | `JSON` | `JSON` | +| `\Model\User` (referenced definition) | | | `TEXT` | `TEXT` | + +## How to use + +Produced file(`postgresql_schema.sql`) contains every table definition. Current implementation doesn't drop or modify existed tables, if you want rewrite whole schema make sure they're not presented. + +### PHPMyAdmin + +1. Choose **Import** tab from the home screen +2. In section **File to import** click to **Choose File** and find generated `postgresql_schema.sql` +3. Make sure **Format** selector set to **SQL** +4. Push **Go** button + +### Adminer + +1. Click **Import** link in left sidebar +2. In **File upload** fieldset click to **Choose Files** and find generated `postgresql_schema.sql` +3. Push **Execute** button + +### Prepared SQL queries + +[Model folder](./Model) contains SQL queries(`SELECT *`, `SELECT`, `INSERT`, `UPDATE`, `DELETE`) usually suggested by `PHPMyAdmin` when you hit `SQL` tab. They are absolutely useless without adaptation to your needs. Copy-paste them then edit. + +Important! Some of SQLs(`INSERT`/`UPDATE`) contains question marks(`?`) which are parameter placeholders. You need to bind values to these params to execute query. + +If your PostgreSQL driver doesn't support named parameters(`PHP PDO` supports while `PHP postgresqli` doesn't) you need to make sure that `namedParametersEnabled` generator option is disabled. diff --git a/samples/schema/petstore/postgresql/postgresql_schema.sql b/samples/schema/petstore/postgresql/postgresql_schema.sql new file mode 100644 index 000000000000..b4506ad6ce15 --- /dev/null +++ b/samples/schema/petstore/postgresql/postgresql_schema.sql @@ -0,0 +1,190 @@ +/* SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; */ +/* SET AUTOCOMMIT = 0; */ +/* START TRANSACTION; */ +/* SET time_zone = "+00:00"; */ + +-- -------------------------------------------------------- + +-- +-- Table structure for table `ApiResponse` generated from model 'ApiResponse' +-- Describes the result of uploading an image resource +-- + +CREATE TABLE IF NOT EXISTS `ApiResponse` ( + `code` INT DEFAULT NULL, + `type` TEXT DEFAULT NULL, + `message` TEXT DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Describes the result of uploading an image resource'; + +-- +-- Table structure for table `Category` generated from model 'Category' +-- A category for a pet +-- + +CREATE TABLE IF NOT EXISTS `Category` ( + `id` BIGINT DEFAULT NULL, + `name` TEXT DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='A category for a pet'; + +-- +-- Table structure for table `Order` generated from model 'Order' +-- An order for a pets from the pet store +-- + +CREATE TABLE IF NOT EXISTS `Order` ( + `id` BIGINT DEFAULT NULL, + `petId` BIGINT DEFAULT NULL, + `quantity` INT DEFAULT NULL, + `shipDate` DATETIME DEFAULT NULL, + `status` ENUM('placed', 'approved', 'delivered') DEFAULT NULL COMMENT 'Order Status', + `complete` TINYINT(1) DEFAULT false +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='An order for a pets from the pet store'; + +-- +-- Table structure for table `Pet` generated from model 'Pet' +-- A pet for sale in the pet store +-- + +CREATE TABLE IF NOT EXISTS `Pet` ( + `id` BIGINT DEFAULT NULL, + `category` TEXT DEFAULT NULL, + `name` TEXT NOT NULL, + `photoUrls` JSON NOT NULL, + `tags` JSON DEFAULT NULL, + `status` ENUM('available', 'pending', 'sold') DEFAULT NULL COMMENT 'pet status in the store' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='A pet for sale in the pet store'; + +-- +-- Table structure for table `Tag` generated from model 'Tag' +-- A tag for a pet +-- + +CREATE TABLE IF NOT EXISTS `Tag` ( + `id` BIGINT DEFAULT NULL, + `name` TEXT DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='A tag for a pet'; + +-- +-- Table structure for table `User` generated from model 'User' +-- A User who is purchasing from the pet store +-- + +CREATE TABLE IF NOT EXISTS `User` ( + `id` BIGINT DEFAULT NULL, + `username` TEXT DEFAULT NULL, + `firstName` TEXT DEFAULT NULL, + `lastName` TEXT DEFAULT NULL, + `email` TEXT DEFAULT NULL, + `password` TEXT DEFAULT NULL, + `phone` TEXT DEFAULT NULL, + `userStatus` INT DEFAULT NULL COMMENT 'User Status' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='A User who is purchasing from the pet store'; + + +-- +-- OAuth2 framework tables +-- Thanks to https://github.com/dsquier/oauth2-server-php-postgresql repo +-- + +-- +-- Table structure for table `oauth_clients` +-- +CREATE TABLE IF NOT EXISTS `oauth_clients` ( + `client_id` VARCHAR(80) NOT NULL, + `client_secret` VARCHAR(80) DEFAULT NULL, + `redirect_uri` VARCHAR(2000) DEFAULT NULL, + `grant_types` VARCHAR(80) DEFAULT NULL, + `scope` VARCHAR(4000) DEFAULT NULL, + `user_id` VARCHAR(80) DEFAULT NULL, + PRIMARY KEY (`client_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_access_tokens` +-- +CREATE TABLE IF NOT EXISTS `oauth_access_tokens` ( + `access_token` VARCHAR(40) NOT NULL, + `client_id` VARCHAR(80) DEFAULT NULL, + `user_id` VARCHAR(80) DEFAULT NULL, + `expires` TIMESTAMP NOT NULL, + `scope` VARCHAR(4000) DEFAULT NULL, + PRIMARY KEY (`access_token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_authorization_codes` +-- +CREATE TABLE IF NOT EXISTS `oauth_authorization_codes` ( + `authorization_code` VARCHAR(40) NOT NULL, + `client_id` VARCHAR(80) DEFAULT NULL, + `user_id` VARCHAR(80) DEFAULT NULL, + `redirect_uri` VARCHAR(2000) NOT NULL, + `expires` TIMESTAMP NOT NULL, + `scope` VARCHAR(4000) DEFAULT NULL, + `id_token` VARCHAR(1000) DEFAULT NULL, + PRIMARY KEY (`authorization_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_refresh_tokens` +-- +CREATE TABLE IF NOT EXISTS `oauth_refresh_tokens` ( + `refresh_token` VARCHAR(40) NOT NULL, + `client_id` VARCHAR(80) DEFAULT NULL, + `user_id` VARCHAR(80) DEFAULT NULL, + `expires` TIMESTAMP on update CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `scope` VARCHAR(4000) DEFAULT NULL, + PRIMARY KEY (`refresh_token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_users` +-- +CREATE TABLE IF NOT EXISTS `oauth_users` ( + `username` VARCHAR(80) DEFAULT NULL, + `password` VARCHAR(255) DEFAULT NULL, + `first_name` VARCHAR(80) DEFAULT NULL, + `last_name` VARCHAR(80) DEFAULT NULL, + `email` VARCHAR(2000) DEFAULT NULL, + `email_verified` TINYINT(1) DEFAULT NULL, + `scope` VARCHAR(4000) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_scopes` +-- +CREATE TABLE IF NOT EXISTS `oauth_scopes` ( + `scope` VARCHAR(80) NOT NULL, + `is_default` TINYINT(1) DEFAULT NULL, + PRIMARY KEY (`scope`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_jwt` +-- +CREATE TABLE IF NOT EXISTS `oauth_jwt` ( + `client_id` VARCHAR(80) NOT NULL, + `subject` VARCHAR(80) DEFAULT NULL, + `public_key` VARCHAR(2000) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_jti` +-- +CREATE TABLE IF NOT EXISTS `oauth_jti` ( + `issuer` VARCHAR(80) NOT NULL, + `subject` VARCHAR(80) DEFAULT NULL, + `audience` VARCHAR(80) DEFAULT NULL, + `expires` TIMESTAMP NOT NULL, + `jti` VARCHAR(2000) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `oauth_public_keys` +-- +CREATE TABLE IF NOT EXISTS `oauth_public_keys` ( + `client_id` VARCHAR(80) DEFAULT NULL, + `public_key` VARCHAR(2000) DEFAULT NULL, + `private_key` VARCHAR(2000) DEFAULT NULL, + `encryption_algorithm` VARCHAR(100) DEFAULT 'RS256' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; From 31529c6202b703bd44e4797fda38646d812ae3dd Mon Sep 17 00:00:00 2001 From: Ingars Ribners Date: Wed, 4 Dec 2024 00:33:59 +0300 Subject: [PATCH 2/9] PostgreSQL schema generator. Initial version. --- bin/utils/openapi-generator-cli.sh | 0 docs/generators/postgresql-schema.md | 278 +-------- .../languages/PostgresqlSchemaCodegen.java | 577 +++++++++++------- .../postgresql_schema.mustache | 178 ++---- .../postgresql_schema_oauth2.mustache | 125 ++++ 5 files changed, 523 insertions(+), 635 deletions(-) mode change 100644 => 100755 bin/utils/openapi-generator-cli.sh create mode 100644 modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema_oauth2.mustache diff --git a/bin/utils/openapi-generator-cli.sh b/bin/utils/openapi-generator-cli.sh old mode 100644 new mode 100755 diff --git a/docs/generators/postgresql-schema.md b/docs/generators/postgresql-schema.md index 150c32037b3f..2635bc96cb0d 100644 --- a/docs/generators/postgresql-schema.md +++ b/docs/generators/postgresql-schema.md @@ -7,11 +7,11 @@ title: Documentation for the postgresql-schema Generator | Property | Value | Notes | | -------- | ----- | ----- | | generator name | postgresql-schema | pass this to the generate command after -g | -| generator stability | STABLE | | +| generator stability | BETA | | | generator type | SCHEMA | | -| generator language | Postgresql | | +| generator language | PostgreSQL | | | generator default templating engine | mustache | | -| helpTxt | Generates a PostgreSQL schema based on the model or schema defined in the OpenAPI specification (v2, v3). | | +| helpTxt | Generates a PostgreSQL schema based on the schema defined in the OpenAPI specification (v2, v3) | | ## CONFIG OPTIONS These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details. @@ -20,7 +20,6 @@ These options may be applied as additional-properties (cli) or configOptions (pl | ------ | ----------- | ------ | ------- | |defaultDatabaseName|Default database name for all PostgreSQL queries| || |identifierNamingConvention|Naming convention of PostgreSQL identifiers(table names and column names). This is not related to database name which is defined by defaultDatabaseName option|
**original**
Do not transform original names
**snake_case**
Use snake_case names
|original| -|jsonDataTypeEnabled|Use special JSON PostgreSQL data type for complex model properties. Requires PostgreSQL version 5.7.8. Generates TEXT data type when disabled| |true| |namedParametersEnabled|Generates model prepared SQLs with named parameters, eg. :petName. Question mark placeholder used when option disabled.| |false| ## IMPORT MAPPING @@ -40,8 +39,6 @@ These options may be applied as additional-properties (cli) or configOptions (pl
  • BigDecimal
  • ByteArray
  • -
  • Date
  • -
  • DateTime
  • URI
  • UUID
  • binary
  • @@ -50,6 +47,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
  • byte
  • char
  • date
  • +
  • DateTime
  • double
  • file
  • float
  • @@ -65,274 +63,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl ## RESERVED WORDS -
      -
    • accessible
    • -
    • add
    • -
    • all
    • -
    • alter
    • -
    • analyze
    • -
    • and
    • -
    • as
    • -
    • asc
    • -
    • asensitive
    • -
    • before
    • -
    • between
    • -
    • bigint
    • -
    • binary
    • -
    • blob
    • -
    • both
    • -
    • by
    • -
    • call
    • -
    • cascade
    • -
    • case
    • -
    • change
    • -
    • char
    • -
    • character
    • -
    • check
    • -
    • collate
    • -
    • column
    • -
    • condition
    • -
    • constraint
    • -
    • continue
    • -
    • convert
    • -
    • create
    • -
    • cross
    • -
    • cube
    • -
    • cume_dist
    • -
    • current_date
    • -
    • current_time
    • -
    • current_timestamp
    • -
    • current_user
    • -
    • cursor
    • -
    • database
    • -
    • databases
    • -
    • day_hour
    • -
    • day_microsecond
    • -
    • day_minute
    • -
    • day_second
    • -
    • dec
    • -
    • decimal
    • -
    • declare
    • -
    • default
    • -
    • delayed
    • -
    • delete
    • -
    • dense_rank
    • -
    • desc
    • -
    • describe
    • -
    • deterministic
    • -
    • distinct
    • -
    • distinctrow
    • -
    • div
    • -
    • double
    • -
    • drop
    • -
    • dual
    • -
    • each
    • -
    • else
    • -
    • elseif
    • -
    • empty
    • -
    • enclosed
    • -
    • escaped
    • -
    • except
    • -
    • exists
    • -
    • exit
    • -
    • explain
    • -
    • false
    • -
    • fetch
    • -
    • first_value
    • -
    • float
    • -
    • float4
    • -
    • float8
    • -
    • for
    • -
    • force
    • -
    • foreign
    • -
    • from
    • -
    • fulltext
    • -
    • function
    • -
    • generated
    • -
    • get
    • -
    • grant
    • -
    • group
    • -
    • grouping
    • -
    • groups
    • -
    • having
    • -
    • high_priority
    • -
    • hour_microsecond
    • -
    • hour_minute
    • -
    • hour_second
    • -
    • if
    • -
    • ignore
    • -
    • in
    • -
    • index
    • -
    • infile
    • -
    • inner
    • -
    • inout
    • -
    • insensitive
    • -
    • insert
    • -
    • int
    • -
    • int1
    • -
    • int2
    • -
    • int3
    • -
    • int4
    • -
    • int8
    • -
    • integer
    • -
    • interval
    • -
    • into
    • -
    • io_after_gtids
    • -
    • io_before_gtids
    • -
    • is
    • -
    • iterate
    • -
    • join
    • -
    • json_table
    • -
    • key
    • -
    • keys
    • -
    • kill
    • -
    • lag
    • -
    • last_value
    • -
    • lead
    • -
    • leading
    • -
    • leave
    • -
    • left
    • -
    • like
    • -
    • limit
    • -
    • linear
    • -
    • lines
    • -
    • load
    • -
    • localtime
    • -
    • localtimestamp
    • -
    • lock
    • -
    • long
    • -
    • longblob
    • -
    • longtext
    • -
    • loop
    • -
    • low_priority
    • -
    • master_bind
    • -
    • master_ssl_verify_server_cert
    • -
    • match
    • -
    • maxvalue
    • -
    • mediumblob
    • -
    • mediumint
    • -
    • mediumtext
    • -
    • middleint
    • -
    • minute_microsecond
    • -
    • minute_second
    • -
    • mod
    • -
    • modifies
    • -
    • natural
    • -
    • no_write_to_binlog
    • -
    • not
    • -
    • nth_value
    • -
    • ntile
    • -
    • null
    • -
    • numeric
    • -
    • of
    • -
    • on
    • -
    • optimize
    • -
    • optimizer_costs
    • -
    • option
    • -
    • optionally
    • -
    • or
    • -
    • order
    • -
    • out
    • -
    • outer
    • -
    • outfile
    • -
    • over
    • -
    • partition
    • -
    • percent_rank
    • -
    • persist
    • -
    • persist_only
    • -
    • precision
    • -
    • primary
    • -
    • procedure
    • -
    • purge
    • -
    • range
    • -
    • rank
    • -
    • read
    • -
    • read_write
    • -
    • reads
    • -
    • real
    • -
    • recursive
    • -
    • references
    • -
    • regexp
    • -
    • release
    • -
    • rename
    • -
    • repeat
    • -
    • replace
    • -
    • require
    • -
    • resignal
    • -
    • restrict
    • -
    • return
    • -
    • revoke
    • -
    • right
    • -
    • rlike
    • -
    • role
    • -
    • row
    • -
    • row_number
    • -
    • rows
    • -
    • schema
    • -
    • schemas
    • -
    • second_microsecond
    • -
    • select
    • -
    • sensitive
    • -
    • separator
    • -
    • set
    • -
    • show
    • -
    • signal
    • -
    • smallint
    • -
    • spatial
    • -
    • specific
    • -
    • sql
    • -
    • sql_big_result
    • -
    • sql_calc_found_rows
    • -
    • sql_small_result
    • -
    • sqlexception
    • -
    • sqlstate
    • -
    • sqlwarning
    • -
    • ssl
    • -
    • starting
    • -
    • stored
    • -
    • straight_join
    • -
    • system
    • -
    • table
    • -
    • terminated
    • -
    • then
    • -
    • tinyblob
    • -
    • tinyint
    • -
    • tinytext
    • -
    • to
    • -
    • trailing
    • -
    • trigger
    • -
    • true
    • -
    • undo
    • -
    • union
    • -
    • unique
    • -
    • unlock
    • -
    • unsigned
    • -
    • update
    • -
    • usage
    • -
    • use
    • -
    • using
    • -
    • utc_date
    • -
    • utc_time
    • -
    • utc_timestamp
    • -
    • values
    • -
    • varbinary
    • -
    • varchar
    • -
    • varcharacter
    • -
    • varying
    • -
    • virtual
    • -
    • when
    • -
    • where
    • -
    • while
    • -
    • window
    • -
    • with
    • -
    • write
    • -
    • xor
    • -
    • year_month
    • -
    • zerofill
    • -
    +[Keywords in the PostgreSQL 17](https://www.postgresql.org/docs/17/sql-keywords-appendix.html) -## FEATURE SET +## FEATURE SET ### Client Modification Feature | Name | Supported | Defined By | diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java index 9c74fb64d67b..7b367e897400 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java @@ -19,9 +19,12 @@ import lombok.Getter; import lombok.Setter; import org.openapitools.codegen.*; +import org.openapitools.codegen.meta.GeneratorMetadata; +import org.openapitools.codegen.meta.Stability; import org.openapitools.codegen.meta.features.*; import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.ModelsMap; +import org.openapitools.codegen.utils.ProcessUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.commons.lang3.StringUtils; @@ -31,10 +34,13 @@ import java.util.regex.Pattern; import java.io.File; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + import static org.openapitools.codegen.utils.StringUtils.underscore; @SuppressWarnings("unchecked") -public class PostgresqlSchemaCodegen extends DefaultCodegen implements CodegenConfig { +public class PostgresqlSchemaCodegen extends DefaultCodegen { private final Logger LOGGER = LoggerFactory.getLogger(PostgresqlSchemaCodegen.class); public static final String VENDOR_EXTENSION_POSTGRESQL_SCHEMA = "x-postgresql-schema"; @@ -43,50 +49,55 @@ public class PostgresqlSchemaCodegen extends DefaultCodegen implements CodegenCo public static final String IDENTIFIER_NAMING_CONVENTION = "identifierNamingConvention"; public static final String NAMED_PARAMETERS_ENABLED = "namedParametersEnabled"; public static final Integer ENUM_MAX_ELEMENTS = 65535; - public static final Integer IDENTIFIER_MAX_LENGTH = 64; + public static final Integer IDENTIFIER_MAX_LENGTH = 63; protected Vector postgresqlNumericTypes = new Vector<>(Arrays.asList( - "BIGINT", "BIT", "BOOL", "BOOLEAN", "DEC", "DECIMAL", "DOUBLE", "DOUBLE PRECISION", "FIXED", "FLOAT", "INT", "INTEGER", "MEDIUMINT", "NUMERIC", "REAL", "SMALLINT", "TINYINT" - )); + "SMALLINT", "INTEGER", "BIGINT", "DECIMAL", "NUMERIC", "REAL", "DOUBLE PRECISION", "SMALLSERIAL", "SERIAL", + "BIGSERIAL")); protected Vector postgresqlDateAndTimeTypes = new Vector<>(Arrays.asList( - "DATE", "DATETIME", "TIME", "TIMESTAMP", "YEAR" - )); + "DATE", "TIME", "TIME WITH TIME ZONE", "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "INTERVAL")); protected Vector postgresqlStringTypes = new Vector<>(Arrays.asList( - "BINARY", "BLOB", "CHAR", "CHAR BYTE", "CHARACTER", "ENUM", "LONGBLOB", "LONGTEXT", "MEDIUMBLOB", "MEDIUMTEXT", "SET", "TEXT", "TINYBLOB", "TINYTEXT", "VARBINARY", "VARCHAR" - )); + "CHAR", "VARCHAR", "TEXT")); protected Vector postgresqlSpatialTypes = new Vector<>(Arrays.asList( - "GEOMETRY", "GEOMETRYCOLLECTION", "LINESTRING", "MULTILINESTRING", "MULTIPOINT", "MULTIPOLYGON", "POINT", "POLYGON" - )); + "POINT", "LINE", "LSEG", "BOX", "PATH", "POLYGON", "CIRCLE")); /** - * Returns default database name for all PostgreSQL queries - * This value must be used with backticks only, e.g. `database_name` + * Returns default database name for all PostgreSQL queries + * This value must be used with backticks only, e.g. `database_name` */ - @Getter protected String defaultDatabaseName = "", databaseNamePrefix = "", databaseNameSuffix = "_db"; + @Getter + protected String defaultDatabaseName = "", databaseNamePrefix = "", databaseNameSuffix = "_db"; protected String tableNamePrefix = "tbl_", tableNameSuffix = ""; protected String columnNamePrefix = "col_", columnNameSuffix = ""; /** - * Whether JSON data type enabled or disabled in all PostgreSQL queries. - * JSON data type requires PostgreSQL version 5.7.8 + * Whether JSON data type enabled or disabled in all PostgreSQL queries. + * JSON data type requires PostgreSQL version 5.7.8 */ - @Getter @Setter + @Getter + @Setter protected Boolean jsonDataTypeEnabled = true; /** - * Whether named parameters enabled or disabled in prepared SQLs + * Whether named parameters enabled or disabled in prepared SQLs */ - @Getter @Setter + @Getter + @Setter protected Boolean namedParametersEnabled = false; /** - * Returns identifier naming convention for table names and column names. + * Returns identifier naming convention for table names and column names. */ - @Getter protected String identifierNamingConvention = "original"; + @Getter + protected String identifierNamingConvention = "original"; public PostgresqlSchemaCodegen() { super(); + generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata) + .stability(Stability.BETA) + .build(); + modifyFeatureSet(features -> features .includeDocumentationFeatures(DocumentationFeature.Readme) .wireFormatFeatures(EnumSet.noneOf(WireFormatFeature.class)) @@ -95,56 +106,77 @@ public PostgresqlSchemaCodegen() { GlobalFeature.XMLStructureDefinitions, GlobalFeature.Callbacks, GlobalFeature.LinkObjects, - GlobalFeature.ParameterStyling - ) + GlobalFeature.ParameterStyling) .excludeSchemaSupportFeatures( - SchemaSupportFeature.Polymorphism - ) - .clientModificationFeatures(EnumSet.noneOf(ClientModificationFeature.class)) - ); - // clear import mapping (from default generator) as postgresql does not use import directives + SchemaSupportFeature.Polymorphism) + .clientModificationFeatures(EnumSet.noneOf(ClientModificationFeature.class))); + // clear import mapping (from default generator) as postgresql does not use + // import directives importMapping.clear(); setModelPackage("Model"); modelTemplateFiles.put("sql_query.mustache", ".sql"); - //modelTestTemplateFiles.put("model_test.mustache", ".php"); - // no doc files - // modelDocTemplateFiles.clear(); - // apiDocTemplateFiles.clear(); - // https://dev.postgresql.com/doc/refman/8.0/en/keywords.html - setReservedWordsLowerCase( - Arrays.asList( - // SQL reserved words - "ACCESSIBLE", "ADD", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ASENSITIVE", - "BEFORE", "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOTH", "BY", - "CALL", "CASCADE", "CASE", "CHANGE", "CHAR", "CHARACTER", "CHECK", "COLLATE", "COLUMN", "CONDITION", "CONSTRAINT", "CONTINUE", "CONVERT", "CREATE", "CROSS", "CUBE", "CUME_DIST", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", - "DATABASE", "DATABASES", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", "DAY_SECOND", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DELAYED", "DELETE", "DENSE_RANK", "DESC", "DESCRIBE", "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DOUBLE", "DROP", "DUAL", - "EACH", "ELSE", "ELSEIF", "EMPTY", "ENCLOSED", "ESCAPED", "EXCEPT", "EXISTS", "EXIT", "EXPLAIN", - "FALSE", "FETCH", "FIRST_VALUE", "FLOAT", "FLOAT4", "FLOAT8", "FOR", "FORCE", "FOREIGN", "FROM", "FULLTEXT", "FUNCTION", - "GENERATED", "GET", "GRANT", "GROUP", "GROUPING", "GROUPS", - "HAVING", "HIGH_PRIORITY", "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", - "IF", "IGNORE", "IN", "INDEX", "INFILE", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", "INT1", "INT2", "INT3", "INT4", "INT8", "INTEGER", "INTERVAL", "INTO", "IO_AFTER_GTIDS", "IO_BEFORE_GTIDS", "IS", "ITERATE", - "JOIN", "JSON_TABLE", - "KEY", "KEYS", "KILL", - "LAG", "LAST_VALUE", "LEAD", "LEADING", "LEAVE", "LEFT", "LIKE", "LIMIT", "LINEAR", "LINES", "LOAD", "LOCALTIME", "LOCALTIMESTAMP", "LOCK", "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", - "MASTER_BIND", "MASTER_SSL_VERIFY_SERVER_CERT", "MATCH", "MAXVALUE", "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", "MIDDLEINT", "MINUTE_MICROSECOND", "MINUTE_SECOND", "MOD", "MODIFIES", - "NATURAL", "NOT", "NO_WRITE_TO_BINLOG", "NTH_VALUE", "NTILE", "NULL", "NUMERIC", - "OF", "ON", "OPTIMIZE", "OPTIMIZER_COSTS", "OPTION", "OPTIONALLY", "OR", "ORDER", "OUT", "OUTER", "OUTFILE", "OVER", - "PARTITION", "PERCENT_RANK", "PERSIST", "PERSIST_ONLY", "PRECISION", "PRIMARY", "PROCEDURE", "PURGE", - "RANGE", "RANK", "READ", "READS", "READ_WRITE", "REAL", "RECURSIVE", "REFERENCES", "REGEXP", "RELEASE", "RENAME", "REPEAT", "REPLACE", "REQUIRE", "RESIGNAL", "RESTRICT", "RETURN", "REVOKE", "RIGHT", "RLIKE", "ROLE", "ROW", "ROWS", "ROW_NUMBER", - "SCHEMA", "SCHEMAS", "SECOND_MICROSECOND", "SELECT", "SENSITIVE", "SEPARATOR", "SET", "SHOW", "SIGNAL", "SMALLINT", "SPATIAL", "SPECIFIC", "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "SQL_BIG_RESULT", "SQL_CALC_FOUND_ROWS", "SQL_SMALL_RESULT", "SSL", "STARTING", "STORED", "STRAIGHT_JOIN", "SYSTEM", - "TABLE", "TERMINATED", "THEN", "TINYBLOB", "TINYINT", "TINYTEXT", "TO", "TRAILING", "TRIGGER", "TRUE", - "UNDO", "UNION", "UNIQUE", "UNLOCK", "UNSIGNED", "UPDATE", "USAGE", "USE", "USING", "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP", - "VALUES", "VARBINARY", "VARCHAR", "VARCHARACTER", "VARYING", "VIRTUAL", - "WHEN", "WHERE", "WHILE", "WINDOW", "WITH", "WRITE", - "XOR", - "YEAR_MONTH", - "ZEROFILL" - ) - ); - - // all types can be threaded as primitives except array, object and refs + // https://www.postgresql.org/docs/17/sql-keywords-appendix.html + setReservedWordsLowerCase( + Arrays.asList( + // SQL reserved words + "ABORT", "ABSOLUTE", "ACCESS", "ACTION", "ADD", "ADMIN", "AFTER", "AGGREGATE", "ALL", + "ALSO", "ALTER", "ALWAYS", "ANALYSE", "ANALYZE", "AND", "ANY", "ARRAY", "AS", "ASC", + "ASSERTION", "ASSIGNMENT", "ASYMMETRIC", "AT", "ATTACH", "ATTRIBUTE", "AUTHORIZATION", + "BACKWARD", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BIT", "BOOLEAN", "BOTH", + "BY", "CACHE", "CALL", "CALLED", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", "CHAIN", + "CHAR", "CHARACTER", "CHARACTERISTICS", "CHECK", "CHECKPOINT", "CLASS", "CLOSE", "CLUSTER", + "COALESCE", "COLLATE", "COLLATION", "COLUMN", "COLUMNS", "COMMENT", "COMMENTS", "COMMIT", + "COMMITTED", "CONCURRENTLY", "CONFIGURATION", "CONFLICT", "CONNECTION", "CONSTRAINT", + "CONSTRAINTS", "CONTENT", "CONTINUE", "CONVERSION", "COPY", "COST", "CREATE", "CROSS", + "CSV", "CUBE", "CURRENT", "CURRENT_CATALOG", "CURRENT_DATE", "CURRENT_ROLE", + "CURRENT_SCHEMA", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "CYCLE", + "DATA", "DATABASE", "DAY", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULTS", + "DEFERRABLE", "DEFERRED", "DEFINE", "DEFINER", "DELETE", "DELIMITER", "DELIMITERS", + "DEPENDS", "DEPTH", "DESC", "DESCRIBE", "DETACH", "DICTIONARY", "DISABLE", "DISCARD", + "DISTINCT", "DO", "DOCUMENT", "DOMAIN", "DOUBLE", "DROP", "EACH", "ELSE", "ENABLE", + "ENCODING", "ENCRYPTED", "END", "ENUM", "ESCAPE", "EVENT", "EXCEPT", "EXCLUDE", "EXCLUDING", + "EXCLUSIVE", "EXECUTE", "EXISTS", "EXPLAIN", "EXTENSION", "EXTERNAL", "EXTRACT", "FALSE", + "FAMILY", "FETCH", "FILTER", "FINALIZE", "FIRST", "FLOAT", "FOLLOWING", "FOR", "FORCE", + "FOREIGN", "FORWARD", "FREEZE", "FROM", "FULL", "FUNCTION", "FUNCTIONS", "GENERATED", + "GLOBAL", "GRANT", "GRANTED", "GREATEST", "GROUP", "GROUPING", "HANDLER", "HAVING", "HEADER", + "HOLD", "HOUR", "IDENTITY", "IF", "ILIKE", "IMMEDIATE", "IMMUTABLE", "IMPLICIT", "IMPORT", + "IN", "INCLUDING", "INCREMENT", "INDEX", "INDEXES", "INHERIT", "INHERITS", "INITIALLY", + "INLINE", "INNER", "INOUT", "INPUT", "INSENSITIVE", "INSERT", "INSTEAD", "INT", "INTEGER", + "INTERSECT", "INTERVAL", "INTO", "INVOKER", "IS", "ISNULL", "ISOLATION", "JOIN", "KEY", + "LABEL", "LANGUAGE", "LARGE", "LAST", "LATERAL", "LEADING", "LEAKPROOF", "LEAST", "LEFT", + "LEVEL", "LIKE", "LIMIT", "LISTEN", "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCATION", + "LOCK", "LOCKED", "LOGGED", "MAPPING", "MATCH", "MATERIALIZED", "MAXVALUE", "METHOD", "MINUTE", + "MINVALUE", "MODE", "MODIFY", "MONTH", "MOVE", "NAME", "NAMES", "NATIONAL", "NATURAL", "NCHAR", + "NEW", "NEXT", "NO", "NONE", "NORMALIZE", "NORMALIZED", "NOT", "NOTHING", "NOTIFY", "NOTNULL", + "NOWAIT", "NULL", "NULLABLE", "NULLIF", "NUMERIC", "OBJECT", "OF", "OFF", "OFFSET", "OIDS", + "OLD", "ON", "ONLY", "OPERATOR", + "OPTION", "OPTIONS", "OR", "ORDER", "ORDINALITY", "OTHERS", "OUT", "OUTER", "OVER", "OVERLAPS", + "OVERLAY", "OVERRIDING", "OWNED", "OWNER", "PARALLEL", "PARAMETER", "PARSER", "PARTIAL", + "PARTITION", "PASSING", "PASSWORD", "PLACING", "PLANS", "POLICY", "POSITION", "PRECEDING", + "PRECISION", "PREPARE", "PREPARED", "PRESERVE", "PRIMARY", "PRIOR", "PRIVILEGES", "PROCEDURAL", + "PROCEDURE", "PROGRAM", "PUBLICATION", "QUOTE", "RANGE", "READ", "REAL", "REASSIGN", "RECHECK", + "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "REFRESH", "REINDEX", "RELATIVE", "RELEASE", + "RENAME", "REPEATABLE", "REPLACE", "REPLICA", "RESET", "RESTART", "RESTRICT", "RETURNING", + "RETURNS", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "ROLLUP", "ROUTINE", "ROUTINES", "ROW", + "ROWS", "RULE", "SAVEPOINT", "SCHEMA", "SCHEMAS", "SCROLL", "SEARCH", "SECOND", "SECURITY", + "SELECT", "SEQUENCE", "SEQUENCES", "SERIALIZABLE", "SERVER", "SESSION", "SESSION_USER", + "SET", "SETOF", "SETS", "SHARE", "SHOW", "SIMILAR", "SIMPLE", "SKIP", "SMALLINT", "SNAPSHOT", + "SOME", "SQL", "STABLE", "STANDALONE", "START", "STATEMENT", "STATISTICS", "STDIN", "STDOUT", + "STORAGE", "STRICT", "STRIP", "SUBSCRIPTION", "SUBSTRING", "SYMMETRIC", "SYSID", "SYSTEM", + "TABLE", "TABLES", "TABLESAMPLE", "TABLESPACE", "TEMP", "TEMPLATE", "TEMPORARY", "TEXT", + "THEN", "TIES", "TIME", "TIMESTAMP", "TO", "TRAILING", "TRANSACTION", "TRANSFORM", "TREAT", + "TRIGGER", "TRIM", "TRUE", "TRUNCATE", "TRUSTED", "TYPE", "TYPES", "UNBOUNDED", "UNCOMMITTED", + "UNENCRYPTED", "UNION", "UNIQUE", "UNKNOWN", "UNLISTEN", "UNLOGGED", "UNTIL", "UPDATE", + "USER", "USING", "VACUUM", "VALID", "VALIDATE", "VALIDATOR", "VALUE", "VALUES", "VARCHAR", + "VARIADIC", "VARYING", "VERBOSE", "VERSION", "VIEW", "VIEWS", "VOLATILE", "WHEN", "WHERE", + "WHITESPACE", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WORK", "WRAPPER", "WRITE", "XML", + "XMLATTRIBUTES", "XMLCONCAT", "XMLELEMENT", "XMLEXISTS", "XMLFOREST", "XMLNAMESPACES", + "XMLPARSE", "XMLPI", "XMLROOT", "XMLSERIALIZE", "XMLTABLE", "YEAR", "YES", "ZONE" + )); + + // primitive data types languageSpecificPrimitives = new HashSet<>( Arrays.asList( "bool", @@ -173,29 +205,28 @@ public PostgresqlSchemaCodegen() { ) ); - // https://dev.postgresql.com/doc/refman/8.0/en/data-types.html + // https://www.postgresql.org/docs/17/datatype.html typeMapping.put("array", "JSON"); typeMapping.put("set", "JSON"); typeMapping.put("map", "JSON"); typeMapping.put("List", "JSON"); - typeMapping.put("boolean", "BOOL"); + typeMapping.put("boolean", "BOOLEAN"); typeMapping.put("string", "TEXT"); typeMapping.put("int", "INT"); typeMapping.put("byte", "TEXT"); typeMapping.put("float", "DECIMAL"); typeMapping.put("number", "DECIMAL"); typeMapping.put("date", "DATE"); - typeMapping.put("Date", "DATETIME"); - typeMapping.put("DateTime", "DATETIME"); + typeMapping.put("DateTime", "TIMESTAMP"); typeMapping.put("long", "BIGINT"); typeMapping.put("short", "SMALLINT"); typeMapping.put("char", "TEXT"); typeMapping.put("double", "DECIMAL"); typeMapping.put("object", "JSON"); typeMapping.put("integer", "INT"); - typeMapping.put("ByteArray", "MEDIUMBLOB"); - typeMapping.put("binary", "MEDIUMBLOB"); - typeMapping.put("file", "MEDIUMBLOB"); + typeMapping.put("ByteArray", "BYTEA"); + typeMapping.put("binary", "BYTEA"); + typeMapping.put("file", "BYTEA"); typeMapping.put("UUID", "TEXT"); typeMapping.put("URI", "TEXT"); typeMapping.put("BigDecimal", "DECIMAL"); @@ -205,12 +236,17 @@ public PostgresqlSchemaCodegen() { // it seems that cli options from DefaultCodegen are useless here cliOptions.clear(); addOption(DEFAULT_DATABASE_NAME, "Default database name for all PostgreSQL queries", defaultDatabaseName); - addSwitch(JSON_DATA_TYPE_ENABLED, "Use special JSON PostgreSQL data type for complex model properties. Requires PostgreSQL version 5.7.8. Generates TEXT data type when disabled", jsonDataTypeEnabled); - addSwitch(NAMED_PARAMETERS_ENABLED, "Generates model prepared SQLs with named parameters, eg. :petName. Question mark placeholder used when option disabled.", namedParametersEnabled); + addSwitch(NAMED_PARAMETERS_ENABLED, + "Generates model prepared SQLs with named parameters, eg. :petName. Question mark placeholder used when option disabled.", + namedParametersEnabled); + // addSwitch(JSON_DATA_TYPE_ENABLED, + // "Use special JSON PostgreSQL data type for complex model properties.", + // jsonDataTypeEnabled); // we used to snake_case table/column names, let's add this option CliOption identifierNamingOpt = new CliOption(IDENTIFIER_NAMING_CONVENTION, - "Naming convention of PostgreSQL identifiers(table names and column names). This is not related to database name which is defined by " + DEFAULT_DATABASE_NAME + " option"); + "Naming convention of PostgreSQL identifiers(table names and column names). This is not related to database name which is defined by " + + DEFAULT_DATABASE_NAME + " option"); identifierNamingOpt.addEnum("original", "Do not transform original names") .addEnum("snake_case", "Use snake_case names") @@ -231,12 +267,19 @@ public String getName() { @Override public String getHelp() { - return "Generates a PostgreSQL schema based on the model or schema defined in the OpenAPI specification (v2, v3)."; + return "Generates a PostgreSQL schema based on the schema defined in the OpenAPI specification (v2, v3)"; } @Override public void processOpts() { + System.out.println("== processOpts 1 ============================= "); super.processOpts(); + System.out.println("== processOpts 2 ============================= "); + + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss"); + String formattedDateTime = now.format(formatter); + additionalProperties.put("currDateTime", formattedDateTime); if (additionalProperties.containsKey(DEFAULT_DATABASE_NAME)) { if (additionalProperties.get(DEFAULT_DATABASE_NAME).equals("")) { @@ -247,6 +290,7 @@ public void processOpts() { additionalProperties.put(DEFAULT_DATABASE_NAME, getDefaultDatabaseName()); } } + System.out.println("== processOpts 3 ============================= "); if (additionalProperties.containsKey(JSON_DATA_TYPE_ENABLED)) { this.setJsonDataTypeEnabled(Boolean.valueOf(additionalProperties.get(JSON_DATA_TYPE_ENABLED).toString())); @@ -254,28 +298,40 @@ public void processOpts() { additionalProperties.put(JSON_DATA_TYPE_ENABLED, getJsonDataTypeEnabled()); } + System.out.println("== processOpts 4 ============================= "); if (additionalProperties.containsKey(NAMED_PARAMETERS_ENABLED)) { - this.setNamedParametersEnabled(Boolean.valueOf(additionalProperties.get(NAMED_PARAMETERS_ENABLED).toString())); + this.setNamedParametersEnabled( + Boolean.valueOf(additionalProperties.get(NAMED_PARAMETERS_ENABLED).toString())); } + System.out.println("== processOpts 5 ============================= "); additionalProperties.put(NAMED_PARAMETERS_ENABLED, getNamedParametersEnabled()); + System.out.println("== processOpts 6 ============================= "); if (additionalProperties.containsKey(IDENTIFIER_NAMING_CONVENTION)) { this.setIdentifierNamingConvention((String) additionalProperties.get(IDENTIFIER_NAMING_CONVENTION)); } + System.out.println("== processOpts 7 ============================= "); // make model src path available in mustache template additionalProperties.put("modelSrcPath", "./" + toSrcPath(modelPackage)); + System.out.println("== processOpts 8 ============================= "); supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); supportingFiles.add(new SupportingFile("postgresql_schema.mustache", "", "postgresql_schema.sql")); + supportingFiles + .add(new SupportingFile("postgresql_schema_oauth2.mustache", "", "postgresql_schema_oauth2.sql")); + System.out.println("== processOpts 9 ============================= "); } @Override public ModelsMap postProcessModels(ModelsMap objs) { + System.out.println("== postProcessModels 1 ============================= "); objs = super.postProcessModels(objs); + System.out.println("== postProcessModels 2 ============================= "); for (ModelMap mo : objs.getModels()) { + System.out.println("== postProcessModels 3 ============================= "); CodegenModel model = mo.getModel(); String modelName = model.getName(); String tableName = this.toTableName(modelName); @@ -287,7 +343,10 @@ public ModelsMap postProcessModels(ModelsMap objs) { if (this.getIdentifierNamingConvention().equals("snake_case") && !modelName.equals(tableName)) { // add original name in table comment String commentExtra = "Original model name - " + modelName + "."; - modelDescription = (modelDescription == null || modelDescription.isEmpty()) ? commentExtra : modelDescription + ". " + commentExtra; + System.out.println(modelDescription); + modelDescription = (modelDescription == null || modelDescription.isEmpty()) ? commentExtra + : modelDescription + ". " + commentExtra; + System.out.println(modelDescription); } if (modelVendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { @@ -297,54 +356,76 @@ public ModelsMap postProcessModels(ModelsMap objs) { modelVendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("tableDefinition", tableDefinition); tableDefinition.put("tblName", tableName); - tableDefinition.put("tblComment", modelDescription); + // tableDefinition.put("tblComment", modelDescription); } } - + System.out.println("== postProcessModels 4 ============================= "); return objs; } @Override public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + System.out.println("== postProcessModelProperty 1 =============================\n\n"); + System.out.println(model.getName()); + System.out.println(property.getName()); + System.out.println(property.getDataType()); switch (property.getDataType().toUpperCase(Locale.ROOT)) { case "BOOL": + System.out.println("== postProcessModelProperty 2 ============================= "); processBooleanTypeProperty(model, property); break; case "TINYINT": case "SMALLINT": case "INT": case "BIGINT": + System.out.println("== postProcessModelProperty 3 ============================= "); processIntegerTypeProperty(model, property); break; case "DECIMAL": + System.out.println("== postProcessModelProperty 4 ============================= "); processDecimalTypeProperty(model, property); break; case "MEDIUMBLOB": case "TEXT": + System.out.println("== postProcessModelProperty 5 ============================= "); processStringTypeProperty(model, property); break; case "DATE": case "DATETIME": + System.out.println("== postProcessModelProperty 6 ============================= "); processDateTypeProperty(model, property); break; case "JSON": + System.out.println("== postProcessModelProperty 7 ============================= "); processJsonTypeProperty(model, property); break; default: + System.out.println("== postProcessModelProperty 8 ============================= "); processUnknownTypeProperty(model, property); } + System.out.println("== postProcessModelProperty 9 =============================\n"); + + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss"); + String formattedDateTime = now.format(formatter); + System.out.println(formattedDateTime); + + System.out.println(additionalProperties.toString()); } /** - * Processes each model's property mapped to integer type and adds related vendor extensions + * Processes each model's property mapped to integer type and adds related + * vendor extensions * * @param model model * @param property model's property */ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty property) { + System.out.println("== processIntegerTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); + Map typeDefinition = new HashMap<>(); ArrayList columnDataTypeArguments = new ArrayList(); String baseName = property.getBaseName(); String colName = this.toColumnName(baseName); @@ -357,7 +438,6 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope boolean exclusiveMaximum = property.getIExclusiveMaximum(); String defaultValue = property.getDefaultValue(); Boolean required = property.getRequired(); - boolean unsigned = false; Boolean isUuid = property.isUuid; Boolean isEnum = property.isEnum; @@ -370,7 +450,8 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { // add original name in column comment String commentExtra = "Original param name - " + baseName + "."; - description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + description = (description == null || description.isEmpty()) ? commentExtra + : description + ". " + commentExtra; } vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); @@ -380,6 +461,11 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope if (Boolean.TRUE.equals(isEnum)) { Map allowableValues = property.getAllowableValues(); List enumValues = (List) allowableValues.get("values"); + String typeName = model.getName() + "_" + property.getName(); + postgresqlSchema.put("typeDefinition", typeDefinition); + columnDefinition.put("colDataType", typeName); + typeDefinition.put("typeName", typeName); + typeDefinition.put("typeArguments", columnDataTypeArguments); for (int i = 0; i < enumValues.size(); i++) { if (i > ENUM_MAX_ELEMENTS - 1) { LOGGER.warn( @@ -390,21 +476,15 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope String value = String.valueOf(enumValues.get(i)); columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(value)); } - columnDefinition.put("colDataType", "ENUM"); - columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); } else { if ("int64".equals(dataFormat)) { columnDefinition.put("colDataType", "BIGINT"); } else { Long min = (minimum != null) ? Long.parseLong(minimum) : null; Long max = (maximum != null) ? Long.parseLong(maximum) : null; - if (exclusiveMinimum && min != null) min += 1; - if (exclusiveMaximum && max != null) max -= 1; - if (min != null && min >= 0) { - unsigned = true; - } - columnDefinition.put("colUnsigned", unsigned); - columnDefinition.put("colDataType", getPostgresqlMatchedIntegerDataType(min, max, unsigned)); + if (exclusiveMinimum && min != null) + min += 1; + columnDefinition.put("colDataType", getPostgresqlMatchedIntegerDataType(min, max, false)); } } @@ -413,7 +493,8 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope } else { columnDefinition.put("colNotNull", false); try { - columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + columnDefinition.put("colDefault", + toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); } catch (RuntimeException exception) { LOGGER.warn( "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", @@ -425,18 +506,23 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope if (description != null) { columnDefinition.put("colComment", description); } + + System.out.println(postgresqlSchema.toString()); } /** - * Processes each model's property mapped to decimal type and adds related vendor extensions + * Processes each model's property mapped to decimal type and adds related + * vendor extensions * * @param model model * @param property model's property */ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty property) { + System.out.println("== processDecimalTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); + Map typeDefinition = new HashMap<>(); ArrayList columnDataTypeArguments = new ArrayList(); String baseName = property.getBaseName(); String colName = this.toColumnName(baseName); @@ -449,7 +535,6 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope boolean exclusiveMaximum = property.getIExclusiveMaximum(); String defaultValue = property.getDefaultValue(); Boolean required = property.getRequired(); - boolean unsigned = false; Boolean isEnum = property.isEnum; if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { @@ -461,7 +546,8 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { // add original name in column comment String commentExtra = "Original param name - " + baseName + "."; - description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + description = (description == null || description.isEmpty()) ? commentExtra + : description + ". " + commentExtra; } vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); @@ -471,6 +557,11 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope if (Boolean.TRUE.equals(isEnum)) { Map allowableValues = property.getAllowableValues(); List enumValues = (List) allowableValues.get("values"); + String typeName = model.getName() + "_" + property.getName(); + postgresqlSchema.put("typeDefinition", typeDefinition); + columnDefinition.put("colDataType", typeName); + typeDefinition.put("typeName", typeName); + typeDefinition.put("typeArguments", columnDataTypeArguments); for (int i = 0; i < enumValues.size(); i++) { if (i > ENUM_MAX_ELEMENTS - 1) { LOGGER.warn( @@ -480,19 +571,16 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope } String value = String.valueOf(enumValues.get(i)); columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(value)); + System.out.println(toCodegenPostgresqlDataTypeArgument(value)); } - columnDefinition.put("colDataType", "ENUM"); - columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); } else { Float min = (minimum != null) ? Float.valueOf(minimum) : null; Float max = (maximum != null) ? Float.valueOf(maximum) : null; - if (exclusiveMinimum && min != null) min += 1; - if (exclusiveMaximum && max != null) max -= 1; - if (min != null && min >= 0) { - unsigned = true; - } + if (exclusiveMinimum && min != null) + min += 1; + if (exclusiveMaximum && max != null) + max -= 1; columnDefinition.put("colDataType", "DECIMAL"); - columnDefinition.put("colUnsigned", unsigned); columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(20)); columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(9)); @@ -503,7 +591,8 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope } else { columnDefinition.put("colNotNull", false); try { - columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + columnDefinition.put("colDefault", + toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); } catch (RuntimeException exception) { LOGGER.warn( "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", @@ -515,24 +604,34 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope if (description != null) { columnDefinition.put("colComment", description); } + + System.out.println(postgresqlSchema.toString()); } /** - * Processes each model's property mapped to boolean type and adds related vendor extensions + * Processes each model's property mapped to string type and adds related vendor + * extensions * * @param model model * @param property model's property */ - public void processBooleanTypeProperty(CodegenModel model, CodegenProperty property) { + public void processStringTypeProperty(CodegenModel model, CodegenProperty property) { + System.out.println("== processStringTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); + Map typeDefinition = new HashMap<>(); ArrayList columnDataTypeArguments = new ArrayList(); String baseName = property.getBaseName(); String colName = this.toColumnName(baseName); + String dataType = property.getDataType(); + String dataFormat = property.getDataFormat(); String description = property.getDescription(); + Integer minLength = property.getMinLength(); + Integer maxLength = property.getMaxLength(); String defaultValue = property.getDefaultValue(); Boolean required = property.getRequired(); + Boolean isEnum = property.isEnum; if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { // user already specified schema values @@ -543,22 +642,50 @@ public void processBooleanTypeProperty(CodegenModel model, CodegenProperty prope if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { // add original name in column comment String commentExtra = "Original param name - " + baseName + "."; - description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + description = (description == null || description.isEmpty()) ? commentExtra + : description + ". " + commentExtra; } vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); - columnDefinition.put("colDataType", "TINYINT"); - columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); - columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(1)); + + if (Boolean.TRUE.equals(isEnum)) { + Map allowableValues = property.getAllowableValues(); + List enumValues = (List) allowableValues.get("values"); + String typeName = model.getName() + "_" + property.getName(); + postgresqlSchema.put("typeDefinition", typeDefinition); + columnDefinition.put("colDataType", typeName); + typeDefinition.put("typeName", typeName); + typeDefinition.put("typeArguments", columnDataTypeArguments); + for (int i = 0; i < enumValues.size(); i++) { + if (i > ENUM_MAX_ELEMENTS - 1) { + LOGGER.warn( + "ENUM column can have maximum of {} distinct elements, following value will be skipped: {}", + ENUM_MAX_ELEMENTS, (String) enumValues.get(i)); + break; + } + String value = String.valueOf(enumValues.get(i)); + columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(value)); + } + } else if (dataType.equals("MEDIUMBLOB")) { + columnDefinition.put("colDataType", "MEDIUMBLOB"); + } else { + String matchedStringType = getPostgresqlMatchedStringDataType(minLength, maxLength); + columnDefinition.put("colDataType", matchedStringType); + if (matchedStringType.equals("CHAR") || matchedStringType.equals("VARCHAR")) { + columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); + columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument((maxLength != null) ? maxLength : 255)); + } + } if (Boolean.TRUE.equals(required)) { columnDefinition.put("colNotNull", true); } else { columnDefinition.put("colNotNull", false); try { - columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + columnDefinition.put("colDefault", + toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); } catch (RuntimeException exception) { LOGGER.warn( "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", @@ -570,29 +697,29 @@ public void processBooleanTypeProperty(CodegenModel model, CodegenProperty prope if (description != null) { columnDefinition.put("colComment", description); } + + System.out.println(postgresqlSchema.toString()); + } /** - * Processes each model's property mapped to string type and adds related vendor extensions + * Processes each model's property mapped to boolean type and adds related + * vendor extensions * * @param model model * @param property model's property */ - public void processStringTypeProperty(CodegenModel model, CodegenProperty property) { + public void processBooleanTypeProperty(CodegenModel model, CodegenProperty property) { + System.out.println("== processBooleanTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); ArrayList columnDataTypeArguments = new ArrayList(); String baseName = property.getBaseName(); String colName = this.toColumnName(baseName); - String dataType = property.getDataType(); - String dataFormat = property.getDataFormat(); String description = property.getDescription(); - Integer minLength = property.getMinLength(); - Integer maxLength = property.getMaxLength(); String defaultValue = property.getDefaultValue(); Boolean required = property.getRequired(); - Boolean isEnum = property.isEnum; if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { // user already specified schema values @@ -603,45 +730,24 @@ public void processStringTypeProperty(CodegenModel model, CodegenProperty proper if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { // add original name in column comment String commentExtra = "Original param name - " + baseName + "."; - description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + description = (description == null || description.isEmpty()) ? commentExtra + : description + ". " + commentExtra; } vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); - - if (Boolean.TRUE.equals(isEnum)) { - Map allowableValues = property.getAllowableValues(); - List enumValues = (List) allowableValues.get("values"); - columnDefinition.put("colDataType", "ENUM"); - columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); - for (int i = 0; i < enumValues.size(); i++) { - if (i > ENUM_MAX_ELEMENTS - 1) { - LOGGER.warn( - "ENUM column can have maximum of {} distinct elements, following value will be skipped: {}", - ENUM_MAX_ELEMENTS, (String) enumValues.get(i)); - break; - } - String value = String.valueOf(enumValues.get(i)); - columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(value)); - } - } else if (dataType.equals("MEDIUMBLOB")) { - columnDefinition.put("colDataType", "MEDIUMBLOB"); - } else { - String matchedStringType = getPostgresqlMatchedStringDataType(minLength, maxLength); - columnDefinition.put("colDataType", matchedStringType); - if (matchedStringType.equals("CHAR") || matchedStringType.equals("VARCHAR")) { - columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); - columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument((maxLength != null) ? maxLength : 255)); - } - } + columnDefinition.put("colDataType", "TINYINT"); + columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); + columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(1)); if (Boolean.TRUE.equals(required)) { columnDefinition.put("colNotNull", true); } else { columnDefinition.put("colNotNull", false); try { - columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + columnDefinition.put("colDefault", + toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); } catch (RuntimeException exception) { LOGGER.warn( "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", @@ -656,12 +762,14 @@ public void processStringTypeProperty(CodegenModel model, CodegenProperty proper } /** - * Processes each model's property mapped to date type and adds related vendor extensions + * Processes each model's property mapped to date type and adds related vendor + * extensions * * @param model model * @param property model's property */ public void processDateTypeProperty(CodegenModel model, CodegenProperty property) { + System.out.println("== processDateTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); @@ -681,7 +789,8 @@ public void processDateTypeProperty(CodegenModel model, CodegenProperty property if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { // add original name in column comment String commentExtra = "Original param name - " + baseName + "."; - description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + description = (description == null || description.isEmpty()) ? commentExtra + : description + ". " + commentExtra; } vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); @@ -694,7 +803,8 @@ public void processDateTypeProperty(CodegenModel model, CodegenProperty property } else { columnDefinition.put("colNotNull", false); try { - columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + columnDefinition.put("colDefault", + toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); } catch (RuntimeException exception) { LOGGER.warn( "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", @@ -709,12 +819,14 @@ public void processDateTypeProperty(CodegenModel model, CodegenProperty property } /** - * Processes each model's property mapped to JSON type and adds related vendor extensions + * Processes each model's property mapped to JSON type and adds related vendor + * extensions * * @param model model * @param property model's property */ public void processJsonTypeProperty(CodegenModel model, CodegenProperty property) { + System.out.println("== processJsonTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); @@ -734,7 +846,8 @@ public void processJsonTypeProperty(CodegenModel model, CodegenProperty property if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { // add original name in column comment String commentExtra = "Original param name - " + baseName + "."; - description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + description = (description == null || description.isEmpty()) ? commentExtra + : description + ". " + commentExtra; } vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); @@ -750,7 +863,8 @@ public void processJsonTypeProperty(CodegenModel model, CodegenProperty property } else { columnDefinition.put("colNotNull", false); try { - columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + columnDefinition.put("colDefault", + toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); } catch (RuntimeException exception) { LOGGER.warn( "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", @@ -765,13 +879,15 @@ public void processJsonTypeProperty(CodegenModel model, CodegenProperty property } /** - * Processes each model's property not mapped to any type and adds related vendor extensions + * Processes each model's property not mapped to any type and adds related + * vendor extensions * Most of time it's related to referenced properties eg. \Model\User * * @param model model * @param property model's property */ public void processUnknownTypeProperty(CodegenModel model, CodegenProperty property) { + System.out.println("== processUnknownTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); @@ -790,7 +906,8 @@ public void processUnknownTypeProperty(CodegenModel model, CodegenProperty prope if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) { // add original name in column comment String commentExtra = "Original param name - " + baseName + "."; - description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra; + description = (description == null || description.isEmpty()) ? commentExtra + : description + ". " + commentExtra; } vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); @@ -803,7 +920,8 @@ public void processUnknownTypeProperty(CodegenModel model, CodegenProperty prope } else { columnDefinition.put("colNotNull", false); try { - columnDefinition.put("colDefault", toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + columnDefinition.put("colDefault", + toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); } catch (RuntimeException exception) { LOGGER.warn( "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", @@ -820,10 +938,11 @@ public void processUnknownTypeProperty(CodegenModel model, CodegenProperty prope /** * Generates codegen property for PostgreSQL data type argument * - * @param value argument value + * @param value argument value * @return generated codegen property */ public HashMap toCodegenPostgresqlDataTypeArgument(Object value) { + System.out.println("== toCodegenPostgresqlDataTypeArgument 1 ============================= "); HashMap arg = new HashMap<>(); if (value instanceof String) { arg.put("isString", true); @@ -841,21 +960,23 @@ public HashMap toCodegenPostgresqlDataTypeArgument(Object value) arg.put("isInteger", false); arg.put("isNumeric", true); } else { - LOGGER.warn("PostgreSQL data type argument can be primitive type only. Class '{}' is provided", value.getClass()); + LOGGER.warn("PostgreSQL data type argument can be primitive type only. Class '{}' is provided", + value.getClass()); } arg.put("argumentValue", value); return arg; } /** - * Generates default codegen property for PostgreSQL column definition - * Ref: https://dev.postgresql.com/doc/refman/5.7/en/data-type-defaults.html + * Generates default value codegen property for PostgreSQL column definition + * Ref: https://www.postgresql.org/docs/17/datatype.html * - * @param defaultValue value + * @param defaultValue value * @param postgresqlDataType PostgreSQL data type * @return generated codegen property */ public HashMap toCodegenPostgresqlDataTypeDefault(String defaultValue, String postgresqlDataType) { + System.out.println("== toCodegenPostgresqlDataTypeDefault 1 ============================= "); HashMap defaultMap = new HashMap<>(); if (defaultValue == null || defaultValue.toUpperCase(Locale.ROOT).equals("NULL")) { defaultMap.put("defaultValue", "NULL"); @@ -871,32 +992,38 @@ public HashMap toCodegenPostgresqlDataTypeDefault(String default case "MEDIUMINT": case "INT": case "BIGINT": - // SERIAL DEFAULT VALUE is a special case. In the definition of an integer column, it is an alias for NOT NULL AUTO_INCREMENT UNIQUE + // SERIAL DEFAULT VALUE is a special case. In the definition of an integer + // column, it is an alias for NOT NULL AUTO_INCREMENT UNIQUE if (defaultValue.equals("SERIAL DEFAULT VALUE")) { defaultMap.put("defaultValue", defaultValue); defaultMap.put("isString", false); defaultMap.put("isNumeric", false); defaultMap.put("isKeyword", true); + } else { defaultMap.put("defaultValue", defaultValue); defaultMap.put("isString", false); defaultMap.put("isNumeric", true); defaultMap.put("isKeyword", false); + } return defaultMap; case "TIMESTAMP": case "DATETIME": - // The exception is that, for TIMESTAMP and DATETIME columns, you can specify CURRENT_TIMESTAMP as the default + // The exception is that, for TIMESTAMP and DATETIME columns, you can specify + // CURRENT_TIMESTAMP as the default if (defaultValue.equals("CURRENT_TIMESTAMP")) { defaultMap.put("defaultValue", defaultValue); defaultMap.put("isString", false); defaultMap.put("isNumeric", false); defaultMap.put("isKeyword", true); + } else { defaultMap.put("defaultValue", defaultValue); defaultMap.put("isString", true); defaultMap.put("isNumeric", false); defaultMap.put("isKeyword", false); + } return defaultMap; case "TINYBLOB": @@ -909,19 +1036,23 @@ public HashMap toCodegenPostgresqlDataTypeDefault(String default case "LONGTEXT": case "GEOMETRY": case "JSON": - // The BLOB, TEXT, GEOMETRY, and JSON data types cannot be assigned a default value. - throw new RuntimeException("The BLOB, TEXT, GEOMETRY, and JSON data types cannot be assigned a default value"); + // The BLOB, TEXT, GEOMETRY, and JSON data types cannot be assigned a default + // value. + throw new RuntimeException( + "The BLOB, TEXT, GEOMETRY, and JSON data types cannot be assigned a default value"); default: defaultMap.put("defaultValue", defaultValue); defaultMap.put("isString", true); defaultMap.put("isNumeric", false); defaultMap.put("isKeyword", false); + return defaultMap; } } /** - * Finds best fitted PostgreSQL data type for integer variable based on minimum and maximum properties + * Finds best fitted PostgreSQL data type for integer variable based on minimum + * and maximum properties * * @param minimum (optional) codegen property * @param maximum (optional) codegen property @@ -929,8 +1060,9 @@ public HashMap toCodegenPostgresqlDataTypeDefault(String default * @return PostgreSQL integer data type */ public String getPostgresqlMatchedIntegerDataType(Long minimum, Long maximum, Boolean unsigned) { + System.out.println("== getPostgresqlMatchedIntegerDataType 1 ============================= "); // we can choose fit postgresql data type - // ref: https://dev.postgresql.com/doc/refman/8.0/en/integer-types.html + // ref: https://www.postgresql.org/docs/17/datatype-numeric.html long min = (minimum != null) ? minimum : -2147483648L; long max = (maximum != null) ? maximum : 2147483647L; long actualMin = Math.min(min, max); // sometimes min and max values can be mixed up @@ -938,45 +1070,28 @@ public String getPostgresqlMatchedIntegerDataType(Long minimum, Long maximum, Bo if (minimum != null && maximum != null && minimum > maximum) { LOGGER.warn("Codegen property 'minimum' cannot be greater than 'maximum'"); } - if (Boolean.TRUE.equals(unsigned) && actualMin >= 0) { - if (actualMax <= 255) { - return "TINYINT"; - } else if (actualMax <= 65535) { - return "SMALLINT"; - } else if (actualMax <= 16777215) { - return "MEDIUMINT"; - } else if (actualMax <= 4294967295L) { - return "INT"; - } else if (actualMax > 4294967295L) { - return "BIGINT"; - } - } else { - if (actualMin >= -128 && actualMax <= 127) { - return "TINYINT"; - } else if (actualMin >= -32768 && actualMax <= 32767) { - return "SMALLINT"; - } else if (actualMin >= -8388608 && actualMax <= 8388607) { - return "MEDIUMINT"; - } else if (actualMin >= -2147483648 && actualMax <= 2147483647) { - return "INT"; - } else if (actualMin < -2147483648 || actualMax > 2147483647) { - return "BIGINT"; - } + if (actualMin >= -32768 && actualMax <= 32767) { + return "SMALLINT"; + } else if (actualMin >= -2147483648 && actualMax <= 2147483647) { + return "INT"; + } else if (actualMin < -2147483648 || actualMax > 2147483647) { + return "BIGINT"; } - return "INT"; } /** - * Finds best fitted PostgreSQL data type for string variable based on minLength and maxLength properties + * Finds best fitted PostgreSQL data type for string variable based on minLength + * and maxLength properties * * @param minLength (optional) codegen property * @param maxLength (optional) codegen property * @return PostgreSQL string data type */ public String getPostgresqlMatchedStringDataType(Integer minLength, Integer maxLength) { + System.out.println("== getPostgresqlMatchedStringDataType 1 ============================= "); // we can choose fit postgresql data type - // ref: https://dev.postgresql.com/doc/refman/8.0/en/string-type-overview.html + // ref: https://www.postgresql.org/docs/17/datatype-character.html int min = (minLength != null && minLength >= 0) ? minLength : 0; int max = (maxLength != null && maxLength >= 0) ? maxLength : 65535; Integer actualMin = Math.min(min, max); // sometimes minLength and maxLength values can be mixed up @@ -984,16 +1099,8 @@ public String getPostgresqlMatchedStringDataType(Integer minLength, Integer maxL if (minLength != null && maxLength != null && minLength > maxLength) { LOGGER.warn("Codegen property 'minLength' cannot be greater than 'maxLength'"); } - if (actualMax.equals(actualMin) && actualMax <= 255) { - return "CHAR"; - } else if (actualMax <= 255) { + if (actualMax <= 65535) { return "VARCHAR"; - } else if (actualMax > 255 && actualMax <= 65535) { - return "TEXT"; - } else if (actualMax > 65535 && actualMax <= 16777215) { - return "MEDIUMTEXT"; - } else if (actualMax > 16777215) { - return "LONGTEXT"; } return "TEXT"; } @@ -1006,18 +1113,16 @@ public String getPostgresqlMatchedStringDataType(Integer minLength, Integer maxL * @return true if value is correct PostgreSQL data type, otherwise false */ public Boolean isPostgresqlDataType(String dataType) { - return ( - postgresqlNumericTypes.contains(dataType.toUpperCase(Locale.ROOT)) || - postgresqlDateAndTimeTypes.contains(dataType.toUpperCase(Locale.ROOT)) || - postgresqlStringTypes.contains(dataType.toUpperCase(Locale.ROOT)) || - postgresqlSpatialTypes.contains(dataType.toUpperCase(Locale.ROOT)) || - dataType.toUpperCase(Locale.ROOT).equals("JSON") - ); + System.out.println("== isPostgresqlDataType 1 ============================= "); + return (postgresqlNumericTypes.contains(dataType.toUpperCase(Locale.ROOT)) || + postgresqlDateAndTimeTypes.contains(dataType.toUpperCase(Locale.ROOT)) || + postgresqlStringTypes.contains(dataType.toUpperCase(Locale.ROOT)) || + postgresqlSpatialTypes.contains(dataType.toUpperCase(Locale.ROOT))); + // || dataType.toUpperCase(Locale.ROOT).equals("JSON")); } /** * Converts name to valid PostgreSQL database name - * Produced name must be used with backticks only, eg. `database_name` * * @param name source name * @return database name @@ -1025,7 +1130,7 @@ public Boolean isPostgresqlDataType(String dataType) { public String toDatabaseName(String name) { String identifier = toPostgresqlIdentifier(name, databaseNamePrefix, databaseNameSuffix); if (identifier.length() > IDENTIFIER_MAX_LENGTH) { - LOGGER.warn("Database name cannot exceed 64 chars. Name '{}' will be truncated", name); + LOGGER.warn("Database name cannot exceed {} chars. Name '{}' will be truncated", IDENTIFIER_MAX_LENGTH, name); identifier = identifier.substring(0, IDENTIFIER_MAX_LENGTH); } return identifier; @@ -1033,7 +1138,6 @@ public String toDatabaseName(String name) { /** * Converts name to valid PostgreSQL column name - * Produced name must be used with backticks only, eg. `table_name` * * @param name source name * @return table name @@ -1044,7 +1148,7 @@ public String toTableName(String name) { identifier = underscore(identifier); } if (identifier.length() > IDENTIFIER_MAX_LENGTH) { - LOGGER.warn("Table name cannot exceed 64 chars. Name '{}' will be truncated", name); + LOGGER.warn("Table name cannot exceed {} chars. Name '{}' will be truncated", IDENTIFIER_MAX_LENGTH, name); identifier = identifier.substring(0, IDENTIFIER_MAX_LENGTH); } return identifier; @@ -1052,7 +1156,6 @@ public String toTableName(String name) { /** * Converts name to valid PostgreSQL column name - * Produced name must be used with backticks only, eg. `column_name` * * @param name source name * @return column name @@ -1063,15 +1166,15 @@ public String toColumnName(String name) { identifier = underscore(identifier); } if (identifier.length() > IDENTIFIER_MAX_LENGTH) { - LOGGER.warn("Column name cannot exceed 64 chars. Name '{}' will be truncated", name); + LOGGER.warn("Column name cannot exceed {} chars. Name '{}' will be truncated", IDENTIFIER_MAX_LENGTH, name); identifier = identifier.substring(0, IDENTIFIER_MAX_LENGTH); } return identifier; } /** - * Converts name to valid PostgreSQL identifier which can be used as database, table, column name - * Produced name must be used with backticks only, eg. `column_name` + * Converts name to valid PostgreSQL identifier which can be used as database, + * table, column name * * @param name source name * @param prefix when escaped name is digits only, prefix will be prepended @@ -1086,7 +1189,8 @@ public String toPostgresqlIdentifier(String name, String prefix, String suffix) escapedName = escapedName.replaceAll("\\s+$", ""); } - // Identifiers may begin with a digit but unless quoted may not consist solely of digits. + // Identifiers may begin with a digit but unless quoted may not consist solely + // of digits. if (escapedName.matches("^\\d+$")) { LOGGER.warn("Database, table, and column names cannot consist solely of digits. Check '{}' name", name); escapedName = prefix + escapedName + suffix; @@ -1100,14 +1204,15 @@ public String toPostgresqlIdentifier(String name, String prefix, String suffix) } /** - * Escapes PostgreSQL identifier to use it in SQL statements without backticks, eg. SELECT identifier FROM - * Ref: https://dev.postgresql.com/doc/refman/8.0/en/identifiers.html + * Escapes PostgreSQL identifier to use it in SQL statements without backticks, + * eg. SELECT identifier FROM * * @param identifier source identifier * @return escaped identifier */ public String escapePostgresqlUnquotedIdentifier(String identifier) { - // ASCII: [0-9,a-z,A-Z$_] (basic Latin letters, digits 0-9, dollar, underscore) Extended: U+0080 .. U+FFFF + // ASCII: [0-9,a-z,A-Z$_] (basic Latin letters, digits 0-9, dollar, underscore) + // Extended: U+0080 .. U+FFFF Pattern regexp = Pattern.compile("[^0-9a-zA-z$_\\u0080-\\uFFFF]"); Matcher matcher = regexp.matcher(identifier); if (matcher.find()) { @@ -1116,15 +1221,17 @@ public String escapePostgresqlUnquotedIdentifier(String identifier) { identifier = identifier.replaceAll("[^0-9a-zA-z$_\\u0080-\\uFFFF]", ""); } - // ASCII NUL (U+0000) and supplementary characters (U+10000 and higher) are not permitted in quoted or unquoted identifiers. - // Don't know how to match these characters, hope that first regexp already strip them + // ASCII NUL (U+0000) and supplementary characters (U+10000 and higher) are not + // permitted in quoted or unquoted identifiers. + // Don't know how to match these characters, hope that first regexp already + // strip them // Pattern regexp2 = Pattern.compile("[\0\uD800\uDC00-\uDBFF\uDFFF]"); return identifier; } /** - * Escapes PostgreSQL identifier to use it in SQL statements with backticks, eg. SELECT `identifier` FROM - * Ref: https://dev.postgresql.com/doc/refman/8.0/en/identifiers.html + * Escapes PostgreSQL identifier to use it in SQL statements with backticks, eg. + * SELECT `identifier` FROM * * @param identifier source identifier * @return escaped identifier @@ -1139,8 +1246,10 @@ public String escapePostgresqlQuotedIdentifier(String identifier) { identifier = identifier.replaceAll("[^\\u0001-\\u007F\\u0080-\\uFFFF]", ""); } - // ASCII NUL (U+0000) and supplementary characters (U+10000 and higher) are not permitted in quoted or unquoted identifiers. - // Don't know how to match these characters, hope that first regexp already strip them + // ASCII NUL (U+0000) and supplementary characters (U+10000 and higher) are not + // permitted in quoted or unquoted identifiers. + // Don't know how to match these characters, hope that first regexp already + // strip them // Pattern regexp2 = Pattern.compile("[\0\uD800\uDC00-\uDBFF\uDFFF]"); return identifier; } @@ -1182,7 +1291,8 @@ public void setDefaultDatabaseName(String databaseName) { /** * Sets identifier naming convention for table names and column names. - * This is not related to database name which is defined by defaultDatabaseName option. + * This is not related to database name which is defined by defaultDatabaseName + * option. * * @param naming identifier naming convention (original|snake_case) */ @@ -1208,15 +1318,16 @@ public void setIdentifierNamingConvention(String naming) { public String toSrcPath(String packageName) { // Trim prefix file separators from package path String packagePath = StringUtils.removeStart( - // Replace period, backslash, forward slash with file separator in package name - packageName.replaceAll("[\\.\\\\/]", Matcher.quoteReplacement("/")), - File.separator - ); + // Replace period, backslash, forward slash with file separator in package name + packageName.replaceAll("[\\.\\\\/]", Matcher.quoteReplacement("/")), + File.separator); // Trim trailing file separators from the overall path return StringUtils.removeEnd(packagePath, File.separator); } @Override - public GeneratorLanguage generatorLanguage() { return GeneratorLanguage.POSTGRESQL; } + public GeneratorLanguage generatorLanguage() { + return GeneratorLanguage.POSTGRESQL; + } } diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache index dfc67a3e81fb..a95746e3a5ca 100644 --- a/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache +++ b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache @@ -1,154 +1,72 @@ -/* SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; */ -/* SET AUTOCOMMIT = 0; */ -/* START TRANSACTION; */ -/* SET time_zone = "+00:00"; */ +-- +-- Schema objects for PostgreSQL {{#defaultDatabaseName}} +-- Database: {{{.}}} +{{/defaultDatabaseName}} +-- Created using 'openapi-generator' ('postgresql-schema' generator) +-- (https://openapi-generator.tech/docs/generators/postgresql-schema) +{{#currDateTime}} -- --- Database: `{{{defaultDatabaseName}}}` +-- Created: {{{.}}} +{{/currDateTime}} -- -CREATE DATABASE IF NOT EXISTS `{{{defaultDatabaseName}}}` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -{{/defaultDatabaseName}} --- -------------------------------------------------------- +-- +-- DROP OBJECTS +-- (remove comment prefix to start using DROP commands) +-- +-- TABLES +-- +{{#models}}{{#model}}{{#hasVars}}{{^isArray}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}-- DROP TABLE IF EXISTS {{#defaultDatabaseName}}{{{.}}}.{{/defaultDatabaseName}}{{tblName}}; +{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/isArray}}{{/hasVars}}{{/model}}{{/models}} +-- +-- TYPES +-- +{{#models}}{{#model}}{{#hasVars}}{{^isArray}}{{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#typeDefinition}}-- DROP TYPE IF EXISTS {{typeName}}; +{{/typeDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}}{{/isArray}}{{/hasVars}}{{/model}}{{/models}} + + +-- +-- CREATE OBJECTS +-- +-- TYPES +-- +{{#models}}{{#model}}{{#hasVars}}{{^isArray}}{{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#typeDefinition}}CREATE TYPE {{typeName}} AS ENUM{{#typeArguments}}{{#-first}}({{/-first}}{{#isString}}'{{/isString}}{{argumentValue}}{{#isString}}'{{/isString}}{{^-last}}, {{/-last}}{{#-last}}); +{{/-last}}{{/typeArguments}}{{/typeDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}}{{/isArray}}{{/hasVars}}{{/model}}{{/models}} + +-- +-- TABLES +-- {{#models}}{{#model}}{{#hasVars}}{{^isArray}}-- --- Table structure{{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}} for table `{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} generated from model '{{classVarName}}' +-- Table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}'{{tblName}}'{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} generated from model '{{classVarName}}' {{#description}} -- {{.}} {{/description}} -- - {{#vendorExtensions}} {{#x-postgresql-schema}} {{#tableDefinition}} -CREATE TABLE IF NOT EXISTS {{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}`{{tblName}}` ( +CREATE TABLE IF NOT EXISTS {{#defaultDatabaseName}}{{{.}}}.{{/defaultDatabaseName}}{{tblName}} ( {{/tableDefinition}} {{/x-postgresql-schema}} {{/vendorExtensions}} - {{#vars}} - {{#vendorExtensions}} - {{#x-postgresql-schema}} - {{#columnDefinition}} - `{{colName}}` {{colDataType}}{{#colDataTypeArguments}}{{#-first}}({{/-first}}{{#isString}}'{{/isString}}{{argumentValue}}{{#isString}}'{{/isString}}{{^-last}}, {{/-last}}{{#-last}}){{/-last}}{{/colDataTypeArguments}}{{#colUnsigned}} UNSIGNED{{/colUnsigned}}{{#colNotNull}} NOT NULL{{/colNotNull}}{{#colDefault}} DEFAULT {{#isString}}'{{defaultValue}}'{{/isString}}{{^isString}}{{defaultValue}}{{/isString}}{{/colDefault}}{{#colComment}} COMMENT '{{.}}'{{/colComment}}{{^-last}},{{/-last}} - {{/columnDefinition}} - {{/x-postgresql-schema}} - {{/vendorExtensions}} - {{/vars}} + {{#vars}} + {{#vendorExtensions}} + {{#x-postgresql-schema}} + {{#columnDefinition}} + {{colName}} {{colDataType}}{{#colNotNull}} NOT NULL{{/colNotNull}}{{#colDefault}} DEFAULT {{#isString}}'{{defaultValue}}'{{/isString}}{{^isString}}{{defaultValue}}{{/isString}}{{/colDefault}}{{^-last}},{{/-last}} + {{/columnDefinition}} + {{/x-postgresql-schema}} + {{/vendorExtensions}} + {{/vars}} {{#vendorExtensions}} {{#x-postgresql-schema}} {{#tableDefinition}} -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci{{#tblComment}} COMMENT='{{.}}'{{/tblComment}}; +); + {{/tableDefinition}} {{/x-postgresql-schema}} {{/vendorExtensions}} - {{/isArray}}{{/hasVars}}{{/model}}{{/models}} -{{#hasOAuthMethods}} --- --- OAuth2 framework tables --- Thanks to https://github.com/dsquier/oauth2-server-php-postgresql repo --- - --- --- Table structure for table `oauth_clients` --- -CREATE TABLE IF NOT EXISTS `oauth_clients` ( - `client_id` VARCHAR(80) NOT NULL, - `client_secret` VARCHAR(80) DEFAULT NULL, - `redirect_uri` VARCHAR(2000) DEFAULT NULL, - `grant_types` VARCHAR(80) DEFAULT NULL, - `scope` VARCHAR(4000) DEFAULT NULL, - `user_id` VARCHAR(80) DEFAULT NULL, - PRIMARY KEY (`client_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; --- --- Table structure for table `oauth_access_tokens` --- -CREATE TABLE IF NOT EXISTS `oauth_access_tokens` ( - `access_token` VARCHAR(40) NOT NULL, - `client_id` VARCHAR(80) DEFAULT NULL, - `user_id` VARCHAR(80) DEFAULT NULL, - `expires` TIMESTAMP NOT NULL, - `scope` VARCHAR(4000) DEFAULT NULL, - PRIMARY KEY (`access_token`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- --- Table structure for table `oauth_authorization_codes` --- -CREATE TABLE IF NOT EXISTS `oauth_authorization_codes` ( - `authorization_code` VARCHAR(40) NOT NULL, - `client_id` VARCHAR(80) DEFAULT NULL, - `user_id` VARCHAR(80) DEFAULT NULL, - `redirect_uri` VARCHAR(2000) NOT NULL, - `expires` TIMESTAMP NOT NULL, - `scope` VARCHAR(4000) DEFAULT NULL, - `id_token` VARCHAR(1000) DEFAULT NULL, - PRIMARY KEY (`authorization_code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- --- Table structure for table `oauth_refresh_tokens` --- -CREATE TABLE IF NOT EXISTS `oauth_refresh_tokens` ( - `refresh_token` VARCHAR(40) NOT NULL, - `client_id` VARCHAR(80) DEFAULT NULL, - `user_id` VARCHAR(80) DEFAULT NULL, - `expires` TIMESTAMP on update CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `scope` VARCHAR(4000) DEFAULT NULL, - PRIMARY KEY (`refresh_token`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- --- Table structure for table `oauth_users` --- -CREATE TABLE IF NOT EXISTS `oauth_users` ( - `username` VARCHAR(80) DEFAULT NULL, - `password` VARCHAR(255) DEFAULT NULL, - `first_name` VARCHAR(80) DEFAULT NULL, - `last_name` VARCHAR(80) DEFAULT NULL, - `email` VARCHAR(2000) DEFAULT NULL, - `email_verified` TINYINT(1) DEFAULT NULL, - `scope` VARCHAR(4000) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- --- Table structure for table `oauth_scopes` --- -CREATE TABLE IF NOT EXISTS `oauth_scopes` ( - `scope` VARCHAR(80) NOT NULL, - `is_default` TINYINT(1) DEFAULT NULL, - PRIMARY KEY (`scope`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- --- Table structure for table `oauth_jwt` --- -CREATE TABLE IF NOT EXISTS `oauth_jwt` ( - `client_id` VARCHAR(80) NOT NULL, - `subject` VARCHAR(80) DEFAULT NULL, - `public_key` VARCHAR(2000) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- --- Table structure for table `oauth_jti` --- -CREATE TABLE IF NOT EXISTS `oauth_jti` ( - `issuer` VARCHAR(80) NOT NULL, - `subject` VARCHAR(80) DEFAULT NULL, - `audience` VARCHAR(80) DEFAULT NULL, - `expires` TIMESTAMP NOT NULL, - `jti` VARCHAR(2000) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- --- Table structure for table `oauth_public_keys` --- -CREATE TABLE IF NOT EXISTS `oauth_public_keys` ( - `client_id` VARCHAR(80) DEFAULT NULL, - `public_key` VARCHAR(2000) DEFAULT NULL, - `private_key` VARCHAR(2000) DEFAULT NULL, - `encryption_algorithm` VARCHAR(100) DEFAULT 'RS256' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -{{/hasOAuthMethods}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema_oauth2.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema_oauth2.mustache new file mode 100644 index 000000000000..6f3fbbf58bfd --- /dev/null +++ b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema_oauth2.mustache @@ -0,0 +1,125 @@ +{{#hasOAuthMethods}} +-- +-- OAuth2 framework tables for PostgreSQL +-- Created using 'openapi-generator' ('postgresql-schema' generator) +-- (https://openapi-generator.tech/docs/generators/postgresql-schema) +{{#currDateTime}} +-- +-- Created: {{{.}}} +{{/currDateTime}} +-- + +-- +-- DROP TABLES +-- (remove comment prefix to start using DROP commands) +-- +-- DROP TABLE IF EXISTS oauth_clients; +-- DROP TABLE IF EXISTS oauth_access_tokens; +-- DROP TABLE IF EXISTS oauth_authorization_codes; +-- DROP TABLE IF EXISTS oauth_refresh_tokens; +-- DROP TABLE IF EXISTS oauth_users; +-- DROP TABLE IF EXISTS oauth_scopes; +-- DROP TABLE IF EXISTS oauth_jwt; +-- DROP TABLE IF EXISTS oauth_jti; +-- DROP TABLE IF EXISTS oauth_public_keys; + + +-- +-- Table oauth_clients +-- +CREATE TABLE IF NOT EXISTS oauth_clients ( + client_id VARCHAR(80) NOT NULL PRIMARY KEY, + client_secret VARCHAR(80) DEFAULT NULL, + redirect_uri VARCHAR(2000) DEFAULT NULL, + grant_types VARCHAR(80) DEFAULT NULL, + scope VARCHAR(4000) DEFAULT NULL, + user_id VARCHAR(80) DEFAULT NULL +); + +-- +-- Table oauth_access_tokens +-- +CREATE TABLE IF NOT EXISTS oauth_access_tokens ( + access_token VARCHAR(40) NOT NULL PRIMARY KEY, + client_id VARCHAR(80) DEFAULT NULL, + user_id VARCHAR(80) DEFAULT NULL, + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000) DEFAULT NULL +); + +-- +-- Table oauth_authorization_codes +-- +CREATE TABLE IF NOT EXISTS oauth_authorization_codes ( + authorization_code VARCHAR(40) NOT NULL PRIMARY KEY, + client_id VARCHAR(80) DEFAULT NULL, + user_id VARCHAR(80) DEFAULT NULL, + redirect_uri VARCHAR(2000) NOT NULL, + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000) DEFAULT NULL, + id_token VARCHAR(1000) DEFAULT NULL +); + +-- +-- Table oauth_refresh_tokens +-- +CREATE TABLE IF NOT EXISTS oauth_refresh_tokens ( + refresh_token VARCHAR(40) NOT NULL PRIMARY KEY, + client_id VARCHAR(80), + user_id VARCHAR(80), + expires TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + scope VARCHAR(4000) +); + +-- +-- Table oauth_users +-- +CREATE TABLE IF NOT EXISTS oauth_users ( + username VARCHAR(80) DEFAULT NULL, + password VARCHAR(255) DEFAULT NULL, + first_name VARCHAR(80) DEFAULT NULL, + last_name VARCHAR(80) DEFAULT NULL, + email VARCHAR(2000) DEFAULT NULL, + email_verified SMALLINT DEFAULT NULL, + scope VARCHAR(4000) DEFAULT NULL +); + +-- +-- Table oauth_scopes +-- +CREATE TABLE IF NOT EXISTS oauth_scopes ( + scope VARCHAR(80) NOT NULL PRIMARY KEY, + is_default SMALLINT DEFAULT NULL +); + +-- +-- Table oauth_jwt +-- +CREATE TABLE IF NOT EXISTS oauth_jwt ( + client_id VARCHAR(80) NOT NULL, + subject VARCHAR(80) DEFAULT NULL, + public_key VARCHAR(2000) NOT NULL +); + +-- +-- Table oauth_jti +-- +CREATE TABLE IF NOT EXISTS oauth_jti ( + issuer VARCHAR(80) NOT NULL, + subject VARCHAR(80) DEFAULT NULL, + audience VARCHAR(80) DEFAULT NULL, + expires TIMESTAMP NOT NULL, + jti VARCHAR(2000) NOT NULL +); + +-- +-- Table oauth_public_keys +-- +CREATE TABLE IF NOT EXISTS oauth_public_keys ( + client_id VARCHAR(80) DEFAULT NULL, + public_key VARCHAR(2000) DEFAULT NULL, + private_key VARCHAR(2000) DEFAULT NULL, + encryption_algorithm VARCHAR(100) DEFAULT 'RS256' +); +{{/hasOAuthMethods}} + From f95b389bdfe2c54a346d821125d326fb8430579c Mon Sep 17 00:00:00 2001 From: Ingars Ribners Date: Thu, 5 Dec 2024 01:25:08 +0300 Subject: [PATCH 3/9] Tested. Fixed errors. Documentation updated. --- README.md | 1 + docs/generators/postgresql-schema.md | 97 +++++- .../languages/PostgresqlSchemaCodegen.java | 293 +++++++++++------- .../postgresql-schema/README.mustache | 60 ++-- .../postgresql_schema.mustache | 22 +- .../postgresql-schema/query_examples.mustache | 32 ++ .../postgresql-schema/sql_query.mustache | 27 -- .../PostgresqlSchemaOptionsProvider.java | 6 +- .../PostgresqlSchemaCodegenTest.java | 20 +- .../PostgresqlSchemaOptionsTest.java | 2 +- 10 files changed, 359 insertions(+), 201 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/postgresql-schema/query_examples.mustache delete mode 100644 modules/openapi-generator/src/main/resources/postgresql-schema/sql_query.mustache diff --git a/README.md b/README.md index 8639ac37741a..08d16048effc 100644 --- a/README.md +++ b/README.md @@ -1139,6 +1139,7 @@ Here is a list of template creators: * GraphQL: @wing328 [:heart:](https://www.patreon.com/wing328) * Ktorm: @Luiz-Monad * MySQL: [@ybelenko](https://github.com/ybelenko) + * PostgreSQL: [@iri](https://github.com/iri) * Postman Collection: @gcatanese * Protocol Buffer: @wing328 * WSDL: @adessoDpd diff --git a/docs/generators/postgresql-schema.md b/docs/generators/postgresql-schema.md index 2635bc96cb0d..a45a9e32da18 100644 --- a/docs/generators/postgresql-schema.md +++ b/docs/generators/postgresql-schema.md @@ -20,7 +20,9 @@ These options may be applied as additional-properties (cli) or configOptions (pl | ------ | ----------- | ------ | ------- | |defaultDatabaseName|Default database name for all PostgreSQL queries| || |identifierNamingConvention|Naming convention of PostgreSQL identifiers(table names and column names). This is not related to database name which is defined by defaultDatabaseName option|
    **original**
    Do not transform original names
    **snake_case**
    Use snake_case names
    |original| +|jsonDataType|Use special JSON data types for complex model properties

    *Note: JSON data type requires PostgreSQL version 9.4 or newer*|
    **json**
    Stores data as a text string.
    - Suitable if you need to preserve the data in its original form in human-readable format.
    - Slower in performing operations because the data has to be parsed each time.
    **jsonb**
    - Stores data in a binary format.
    - Supports indexing, which makes search and data retrieval operations faster.
    - Does not preserve the order of keys and may change formatting (e.g., removing extra spaces)
    **off**
    JSON data types disabled. TEXT type will be used to store JSON data
    |json| |namedParametersEnabled|Generates model prepared SQLs with named parameters, eg. :petName. Question mark placeholder used when option disabled.| |false| +|idAutoIncEnabled|Add an auto-increment feature for 'id' fields of integer type.|
    **true**
    This generates SERIAL/BIGSERIAL type instead of INT/BIGINT, which automatically handle 'id' field value increments when inserting data into table.
    The choice between SERIAL and BIGSERIAL is based on the format of the OpenAPI schema object 'id' property (int32 to SERIAL and int64 to BIGSERIAL respectively)
    **false**
    - Leave integer 'id' field type as is (INT/BIGINT)
    |false| ## IMPORT MAPPING @@ -63,7 +65,98 @@ These options may be applied as additional-properties (cli) or configOptions (pl ## RESERVED WORDS -[Keywords in the PostgreSQL 17](https://www.postgresql.org/docs/17/sql-keywords-appendix.html) +| ABORT | ABSOLUTE | ACCESS | ACTION | ADD | +|------------|-----------|------------|-----------|-----------| +| ADMIN | AFTER | AGGREGATE | ALL | ALSO | +| ALTER | ALWAYS | ANALYSE | ANALYZE | AND | +| ANY | ARRAY | AS | ASC | ASSERTION | +| ASSIGNMENT | ASYMMETRIC| AT | ATTACH | ATTRIBUTE | +| AUTHORIZATION | BACKWARD | BEFORE | BEGIN | BETWEEN | +| BIGINT | BINARY | BIT | BOOLEAN | BOTH | +| BY | CACHE | CALL | CALLED | CASCADE | +| CASCADED | CASE | CAST | CATALOG | CHAIN | +| CHAR | CHARACTER | CHARACTERISTICS | CHECK | CHECKPOINT| +| CLASS | CLOSE | CLUSTER | COALESCE | COLLATE | +| COLLATION | COLUMN | COLUMNS | COMMENT | COMMENTS | +| COMMIT | COMMITTED | CONCURRENTLY | CONFIGURATION | CONFLICT | +| CONNECTION | CONSTRAINT| CONSTRAINTS| CONTENT | CONTINUE | +| CONVERSION | COPY | COST | CREATE | CROSS | +| CSV | CUBE | CURRENT | CURRENT_CATALOG | CURRENT_DATE | +| CURRENT_ROLE | CURRENT_SCHEMA | CURRENT_TIME | CURRENT_TIMESTAMP | CURRENT_USER | +| CURSOR | CYCLE | DATA | DATABASE | DAY | +| DEALLOCATE | DEC | DECIMAL | DECLARE | DEFAULT | +| DEFAULTS | DEFERRABLE| DEFERRED | DEFINE | DEFINER | +| DELETE | DELIMITER | DELIMITERS | DEPENDS | DEPTH | +| DESC | DESCRIBE | DETACH | DICTIONARY| DISABLE | +| DISCARD | DISTINCT | DO | DOCUMENT | DOMAIN | +| DOUBLE | DROP | EACH | ELSE | ENABLE | +| ENCODING | ENCRYPTED | END | ENUM | ESCAPE | +| EVENT | EXCEPT | EXCLUDE | EXCLUDING | EXCLUSIVE | +| EXECUTE | EXISTS | EXPLAIN | EXTENSION | EXTERNAL | +| EXTRACT | FALSE | FAMILY | FETCH | FILTER | +| FINALIZE | FIRST | FLOAT | FOLLOWING | FOR | +| FORCE | FOREIGN | FORWARD | FREEZE | FROM | +| FULL | FUNCTION | FUNCTIONS | GENERATED | GLOBAL | +| GRANT | GRANTED | GREATEST | GROUP | GROUPING | +| HANDLER | HAVING | HEADER | HOLD | HOUR | +| IDENTITY | IF | ILIKE | IMMEDIATE | IMMUTABLE | +| IMPLICIT | IMPORT | IN | INCLUDING | INCREMENT | +| INDEX | INDEXES | INHERIT | INHERITS | INITIALLY | +| INLINE | INNER | INOUT | INPUT | INSENSITIVE | +| INSERT | INSTEAD | INT | INTEGER | INTERSECT | +| INTERVAL | INTO | INVOKER | IS | ISNULL | +| ISOLATION | JOIN | KEY | LABEL | LANGUAGE | +| LARGE | LAST | LATERAL | LEADING | LEAKPROOF | +| LEAST | LEFT | LEVEL | LIKE | LIMIT | +| LISTEN | LOAD | LOCAL | LOCALTIME | LOCALTIMESTAMP | +| LOCATION | LOCK | LOCKED | LOGGED | MAPPING | +| MATCH | MATERIALIZED | MAXVALUE | METHOD | MINUTE | +| MINVALUE | MODE | MODIFY | MONTH | MOVE | +| NAME | NAMES | NATIONAL | NATURAL | NCHAR | +| NEW | NEXT | NO | NONE | NORMALIZE | +| NORMALIZED | NOT | NOTHING | NOTIFY | NOTNULL | +| NOWAIT | NULL | NULLABLE | NULLIF | NUMERIC | +| OBJECT | OF | OFF | OFFSET | OIDS | +| OLD | ON | ONLY | OPERATOR | OPTION | +| OPTIONS | OR | ORDER | ORDINALITY | OTHERS | +| OUT | OUTER | OVER | OVERLAPS | OVERLAY | +| OVERRIDING | OWNED | OWNER | PARALLEL | PARAMETER | +| PARSER | PARTIAL | PARTITION | PASSING | PASSWORD | +| PLACING | PLANS | POLICY | POSITION | PRECEDING | +| PRECISION | PREPARE | PREPARED | PRESERVE | PRIMARY | +| PRIOR | PRIVILEGES | PROCEDURAL | PROCEDURE | PROGRAM | +| PUBLICATION| QUOTE | RANGE | READ | REAL | +| REASSIGN | RECHECK | RECURSIVE | REF | REFERENCES | +| REFERENCING| REFRESH | REINDEX | RELATIVE | RELEASE | +| RENAME | REPEATABLE | REPLACE | REPLICA | RESET | +| RESTART | RESTRICT | RETURNING | RETURNS | REVOKE | +| RIGHT | ROLE | ROLLBACK | ROLLUP | ROUTINE | +| ROUTINES | ROW | ROWS | RULE | SAVEPOINT | +| SCHEMA | SCHEMAS | SCROLL | SEARCH | SECOND | +| SECURITY | SELECT | SEQUENCE | SEQUENCES | SERIALIZABLE| +| SERVER | SESSION | SESSION_USER| SET | SETOF | +| SETS | SHARE | SHOW | SIMILAR | SIMPLE | +| SKIP | SMALLINT | SNAPSHOT | SOME | SQL | +| STABLE | STANDALONE | START | STATEMENT | STATISTICS | +| STDIN | STDOUT | STORAGE | STRICT | STRIP | +| SUBSCRIPTION| SUBSTRING | SYMMETRIC | SYSID | SYSTEM | +| TABLE | TABLES | TABLESAMPLE| TABLESPACE | TEMP | +| TEMPLATE | TEMPORARY | TEXT | THEN | TIES | +| TIME | TIMESTAMP | TO | TRAILING | TRANSACTION| +| TRANSFORM | TREAT | TRIGGER | TRIM | TRUE | +| TRUNCATE | TRUSTED | TYPE | TYPES | UNBOUNDED | +| UNCOMMITTED| UNENCRYPTED| UNION | UNIQUE | UNKNOWN | +| UNLISTEN | UNLOGGED | UNTIL | UPDATE | USER | +| USING | VACUUM | VALID | VALIDATE | VALIDATOR | +| VALUE | VALUES | VARCHAR | VARIADIC | VARYING | +| VERBOSE | VERSION | VIEW | VIEWS | VOLATILE | +| WHEN | WHERE | WHITESPACE | WINDOW | WITH | +| WITHIN | WITHOUT | WORK | WRAPPER | WRITE | +| XML | XMLATTRIBUTES| XMLCONCAT | XMLELEMENT | XMLEXISTS | +| XMLFOREST | XMLNAMESPACES| XMLPARSE | XMLPI | XMLROOT | +| XMLSERIALIZE| XMLTABLE | YEAR | YES | ZONE | + +Source: [Full list of keywords in the PostgreSQL 17](https://www.postgresql.org/docs/17/sql-keywords-appendix.html) ## FEATURE SET @@ -78,7 +171,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl ### Data Type Feature | Name | Supported | Defined By | -| ---- | --------- | ---------- | +| ---- | --------- | ---------- |s |Custom|✗|OAS2,OAS3 |Int32|✓|OAS2,OAS3 |Int64|✓|OAS2,OAS3 diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java index 7b367e897400..b6b7914e6b32 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java @@ -45,9 +45,10 @@ public class PostgresqlSchemaCodegen extends DefaultCodegen { public static final String VENDOR_EXTENSION_POSTGRESQL_SCHEMA = "x-postgresql-schema"; public static final String DEFAULT_DATABASE_NAME = "defaultDatabaseName"; - public static final String JSON_DATA_TYPE_ENABLED = "jsonDataTypeEnabled"; + public static final String JSON_DATA_TYPE = "jsonDataType"; public static final String IDENTIFIER_NAMING_CONVENTION = "identifierNamingConvention"; public static final String NAMED_PARAMETERS_ENABLED = "namedParametersEnabled"; + public static final String ID_AUTOINC_ENABLED = "idAutoIncEnabled"; public static final Integer ENUM_MAX_ELEMENTS = 65535; public static final Integer IDENTIFIER_MAX_LENGTH = 63; @@ -73,12 +74,12 @@ public class PostgresqlSchemaCodegen extends DefaultCodegen { protected String tableNamePrefix = "tbl_", tableNameSuffix = ""; protected String columnNamePrefix = "col_", columnNameSuffix = ""; /** - * Whether JSON data type enabled or disabled in all PostgreSQL queries. - * JSON data type requires PostgreSQL version 5.7.8 + * Which type of JSON data types will be used. + * JSON data type requires PostgreSQL version 9.4 or newer */ @Getter @Setter - protected Boolean jsonDataTypeEnabled = true; + protected String jsonDataType = "json"; /** * Whether named parameters enabled or disabled in prepared SQLs */ @@ -90,6 +91,12 @@ public class PostgresqlSchemaCodegen extends DefaultCodegen { */ @Getter protected String identifierNamingConvention = "original"; + /** + * Whether autoincrement feature enabled for integer 'id' fields + */ + @Getter + @Setter + protected Boolean idAutoIncEnabled = false; public PostgresqlSchemaCodegen() { super(); @@ -115,7 +122,7 @@ public PostgresqlSchemaCodegen() { importMapping.clear(); setModelPackage("Model"); - modelTemplateFiles.put("sql_query.mustache", ".sql"); + modelTemplateFiles.put("query_examples.mustache", ".sql"); // https://www.postgresql.org/docs/17/sql-keywords-appendix.html setReservedWordsLowerCase( @@ -193,7 +200,6 @@ public PostgresqlSchemaCodegen() { "short", "char", "ByteArray", - "binary", "file", "UUID", "URI", @@ -225,7 +231,6 @@ public PostgresqlSchemaCodegen() { typeMapping.put("object", "JSON"); typeMapping.put("integer", "INT"); typeMapping.put("ByteArray", "BYTEA"); - typeMapping.put("binary", "BYTEA"); typeMapping.put("file", "BYTEA"); typeMapping.put("UUID", "TEXT"); typeMapping.put("URI", "TEXT"); @@ -235,14 +240,18 @@ public PostgresqlSchemaCodegen() { // it seems that cli options from DefaultCodegen are useless here cliOptions.clear(); - addOption(DEFAULT_DATABASE_NAME, "Default database name for all PostgreSQL queries", defaultDatabaseName); + addOption(DEFAULT_DATABASE_NAME, + "Default database name for all PostgreSQL queries", defaultDatabaseName); addSwitch(NAMED_PARAMETERS_ENABLED, - "Generates model prepared SQLs with named parameters, eg. :petName. Question mark placeholder used when option disabled.", + "Generates model prepared SQLs with named parameters, eg. :petName. Question mark placeholder used when option disabled.", namedParametersEnabled); - // addSwitch(JSON_DATA_TYPE_ENABLED, - // "Use special JSON PostgreSQL data type for complex model properties.", - // jsonDataTypeEnabled); - + addOption(JSON_DATA_TYPE, + "Use special JSON PostgreSQL data type for complex model properties.", + jsonDataType); + addSwitch(ID_AUTOINC_ENABLED, + "Generates PostgreSQL sequences for autoincrement feature for integer 'id' fields.", + idAutoIncEnabled); + // we used to snake_case table/column names, let's add this option CliOption identifierNamingOpt = new CliOption(IDENTIFIER_NAMING_CONVENTION, "Naming convention of PostgreSQL identifiers(table names and column names). This is not related to database name which is defined by " @@ -272,9 +281,7 @@ public String getHelp() { @Override public void processOpts() { - System.out.println("== processOpts 1 ============================= "); super.processOpts(); - System.out.println("== processOpts 2 ============================= "); LocalDateTime now = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss"); @@ -290,48 +297,45 @@ public void processOpts() { additionalProperties.put(DEFAULT_DATABASE_NAME, getDefaultDatabaseName()); } } - System.out.println("== processOpts 3 ============================= "); - if (additionalProperties.containsKey(JSON_DATA_TYPE_ENABLED)) { - this.setJsonDataTypeEnabled(Boolean.valueOf(additionalProperties.get(JSON_DATA_TYPE_ENABLED).toString())); - } else { - additionalProperties.put(JSON_DATA_TYPE_ENABLED, getJsonDataTypeEnabled()); + if (additionalProperties.containsKey(JSON_DATA_TYPE)) { + if (additionalProperties.get(JSON_DATA_TYPE).equals("off")) { + this.setJsonDataType("off"); + } else if (additionalProperties.get(JSON_DATA_TYPE).equals("jsonb")) { + this.setJsonDataType("jsonb"); + } } - System.out.println("== processOpts 4 ============================= "); if (additionalProperties.containsKey(NAMED_PARAMETERS_ENABLED)) { this.setNamedParametersEnabled( Boolean.valueOf(additionalProperties.get(NAMED_PARAMETERS_ENABLED).toString())); } - - System.out.println("== processOpts 5 ============================= "); additionalProperties.put(NAMED_PARAMETERS_ENABLED, getNamedParametersEnabled()); - System.out.println("== processOpts 6 ============================= "); + if (additionalProperties.containsKey(ID_AUTOINC_ENABLED)) { + this.setIdAutoIncEnabled( + Boolean.valueOf(additionalProperties.get(ID_AUTOINC_ENABLED).toString())); + } + additionalProperties.put(ID_AUTOINC_ENABLED, getIdAutoIncEnabled()); + if (additionalProperties.containsKey(IDENTIFIER_NAMING_CONVENTION)) { this.setIdentifierNamingConvention((String) additionalProperties.get(IDENTIFIER_NAMING_CONVENTION)); } - System.out.println("== processOpts 7 ============================= "); // make model src path available in mustache template additionalProperties.put("modelSrcPath", "./" + toSrcPath(modelPackage)); - System.out.println("== processOpts 8 ============================= "); supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); supportingFiles.add(new SupportingFile("postgresql_schema.mustache", "", "postgresql_schema.sql")); supportingFiles .add(new SupportingFile("postgresql_schema_oauth2.mustache", "", "postgresql_schema_oauth2.sql")); - System.out.println("== processOpts 9 ============================= "); } @Override public ModelsMap postProcessModels(ModelsMap objs) { - System.out.println("== postProcessModels 1 ============================= "); objs = super.postProcessModels(objs); - System.out.println("== postProcessModels 2 ============================= "); for (ModelMap mo : objs.getModels()) { - System.out.println("== postProcessModels 3 ============================= "); CodegenModel model = mo.getModel(); String modelName = model.getName(); String tableName = this.toTableName(modelName); @@ -343,10 +347,8 @@ public ModelsMap postProcessModels(ModelsMap objs) { if (this.getIdentifierNamingConvention().equals("snake_case") && !modelName.equals(tableName)) { // add original name in table comment String commentExtra = "Original model name - " + modelName + "."; - System.out.println(modelDescription); modelDescription = (modelDescription == null || modelDescription.isEmpty()) ? commentExtra : modelDescription + ". " + commentExtra; - System.out.println(modelDescription); } if (modelVendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { @@ -356,61 +358,44 @@ public ModelsMap postProcessModels(ModelsMap objs) { modelVendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("tableDefinition", tableDefinition); tableDefinition.put("tblName", tableName); - // tableDefinition.put("tblComment", modelDescription); + tableDefinition.put("tblComment", modelDescription); + if (isReservedWord(tableName)) { // Output table name in double quotes if it is a reserved word + tableDefinition.put("tblNameQuoted", true); + } } } - System.out.println("== postProcessModels 4 ============================= "); return objs; } @Override public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { - System.out.println("== postProcessModelProperty 1 =============================\n\n"); - System.out.println(model.getName()); - System.out.println(property.getName()); - System.out.println(property.getDataType()); switch (property.getDataType().toUpperCase(Locale.ROOT)) { - case "BOOL": - System.out.println("== postProcessModelProperty 2 ============================= "); + case "BOOLEAN": processBooleanTypeProperty(model, property); break; - case "TINYINT": case "SMALLINT": case "INT": case "BIGINT": - System.out.println("== postProcessModelProperty 3 ============================= "); processIntegerTypeProperty(model, property); break; case "DECIMAL": - System.out.println("== postProcessModelProperty 4 ============================= "); processDecimalTypeProperty(model, property); break; - case "MEDIUMBLOB": case "TEXT": - System.out.println("== postProcessModelProperty 5 ============================= "); + case "BYTEA": processStringTypeProperty(model, property); break; case "DATE": - case "DATETIME": - System.out.println("== postProcessModelProperty 6 ============================= "); + case "TIMESTAMP": processDateTypeProperty(model, property); break; case "JSON": - System.out.println("== postProcessModelProperty 7 ============================= "); + case "JSONB": processJsonTypeProperty(model, property); break; default: - System.out.println("== postProcessModelProperty 8 ============================= "); processUnknownTypeProperty(model, property); } - System.out.println("== postProcessModelProperty 9 =============================\n"); - - LocalDateTime now = LocalDateTime.now(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss"); - String formattedDateTime = now.format(formatter); - System.out.println(formattedDateTime); - - System.out.println(additionalProperties.toString()); } /** @@ -421,7 +406,6 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert * @param property model's property */ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty property) { - System.out.println("== processIntegerTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); @@ -457,6 +441,17 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + columnDefinition.put("colNameQuoted", true); + } else { + columnDefinition.put("colNameQuoted", false); + } + columnDefinition.put("tblName", model.getName()); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblNameQuoted", true); + } else { + columnDefinition.put("tblNameQuoted", false); + } if (Boolean.TRUE.equals(isEnum)) { Map allowableValues = property.getAllowableValues(); @@ -478,36 +473,49 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope } } else { if ("int64".equals(dataFormat)) { - columnDefinition.put("colDataType", "BIGINT"); + if (colName.equals("id") && Boolean.valueOf(additionalProperties.get(ID_AUTOINC_ENABLED).toString())) { + columnDefinition.put("colDataType", "BIGSERIAL"); + } else { + columnDefinition.put("colDataType", "BIGINT"); + } } else { Long min = (minimum != null) ? Long.parseLong(minimum) : null; Long max = (maximum != null) ? Long.parseLong(maximum) : null; if (exclusiveMinimum && min != null) min += 1; - columnDefinition.put("colDataType", getPostgresqlMatchedIntegerDataType(min, max, false)); + String colDataType = getPostgresqlMatchedIntegerDataType(min, max, false); + if (colName.equals("id") && Boolean.valueOf(additionalProperties.get(ID_AUTOINC_ENABLED).toString())) { + if (colDataType.equals("BIGINT")) { + colDataType = "BIGSERIAL"; + } else { + colDataType = "SERIAL"; + } + } + columnDefinition.put("colDataType", colDataType); } } - if (Boolean.TRUE.equals(required)) { - columnDefinition.put("colNotNull", true); - } else { - columnDefinition.put("colNotNull", false); - try { - columnDefinition.put("colDefault", - toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); - } catch (RuntimeException exception) { - LOGGER.warn( - "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", - baseName, model.getName()); - columnDefinition.put("colDefault", null); + if ( !columnDefinition.get("colDataType").equals("SERIAL") + && !columnDefinition.get("colDataType").equals("BIGSERIAL")) { // No default value for autoincremented IDs + if (Boolean.TRUE.equals(required)) { + columnDefinition.put("colNotNull", true); + } else { + columnDefinition.put("colNotNull", false); + try { + columnDefinition.put("colDefault", + toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + } catch (RuntimeException exception) { + LOGGER.warn( + "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", + baseName, model.getName()); + columnDefinition.put("colDefault", null); + } } } if (description != null) { columnDefinition.put("colComment", description); } - - System.out.println(postgresqlSchema.toString()); } /** @@ -518,7 +526,6 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope * @param property model's property */ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty property) { - System.out.println("== processDecimalTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); @@ -553,6 +560,17 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + columnDefinition.put("colNameQuoted", true); + } else { + columnDefinition.put("colNameQuoted", false); + } + columnDefinition.put("tblName", model.getName()); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblNameQuoted", true); + } else { + columnDefinition.put("tblNameQuoted", false); + } if (Boolean.TRUE.equals(isEnum)) { Map allowableValues = property.getAllowableValues(); @@ -571,7 +589,6 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope } String value = String.valueOf(enumValues.get(i)); columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(value)); - System.out.println(toCodegenPostgresqlDataTypeArgument(value)); } } else { Float min = (minimum != null) ? Float.valueOf(minimum) : null; @@ -604,8 +621,6 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope if (description != null) { columnDefinition.put("colComment", description); } - - System.out.println(postgresqlSchema.toString()); } /** @@ -616,7 +631,6 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope * @param property model's property */ public void processStringTypeProperty(CodegenModel model, CodegenProperty property) { - System.out.println("== processStringTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); @@ -649,6 +663,17 @@ public void processStringTypeProperty(CodegenModel model, CodegenProperty proper vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + columnDefinition.put("colNameQuoted", true); + } else { + columnDefinition.put("colNameQuoted", false); + } + columnDefinition.put("tblName", model.getName()); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblNameQuoted", true); + } else { + columnDefinition.put("tblNameQuoted", false); + } if (Boolean.TRUE.equals(isEnum)) { Map allowableValues = property.getAllowableValues(); @@ -668,12 +693,12 @@ public void processStringTypeProperty(CodegenModel model, CodegenProperty proper String value = String.valueOf(enumValues.get(i)); columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(value)); } - } else if (dataType.equals("MEDIUMBLOB")) { - columnDefinition.put("colDataType", "MEDIUMBLOB"); + } else if (dataType.equals("BYTEA")) { + columnDefinition.put("colDataType", "BYTEA"); } else { String matchedStringType = getPostgresqlMatchedStringDataType(minLength, maxLength); columnDefinition.put("colDataType", matchedStringType); - if (matchedStringType.equals("CHAR") || matchedStringType.equals("VARCHAR")) { + if (matchedStringType.equals("VARCHAR")) { columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument((maxLength != null) ? maxLength : 255)); } @@ -698,8 +723,6 @@ public void processStringTypeProperty(CodegenModel model, CodegenProperty proper columnDefinition.put("colComment", description); } - System.out.println(postgresqlSchema.toString()); - } /** @@ -710,7 +733,6 @@ public void processStringTypeProperty(CodegenModel model, CodegenProperty proper * @param property model's property */ public void processBooleanTypeProperty(CodegenModel model, CodegenProperty property) { - System.out.println("== processBooleanTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); @@ -737,9 +759,19 @@ public void processBooleanTypeProperty(CodegenModel model, CodegenProperty prope vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); - columnDefinition.put("colDataType", "TINYINT"); - columnDefinition.put("colDataTypeArguments", columnDataTypeArguments); - columnDataTypeArguments.add(toCodegenPostgresqlDataTypeArgument(1)); + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + columnDefinition.put("colNameQuoted", true); + } else { + columnDefinition.put("colNameQuoted", false); + } + columnDefinition.put("tblName", model.getName()); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblNameQuoted", true); + } else { + columnDefinition.put("tblNameQuoted", false); + } + + columnDefinition.put("colDataType", "BOOLEAN"); if (Boolean.TRUE.equals(required)) { columnDefinition.put("colNotNull", true); @@ -769,7 +801,6 @@ public void processBooleanTypeProperty(CodegenModel model, CodegenProperty prope * @param property model's property */ public void processDateTypeProperty(CodegenModel model, CodegenProperty property) { - System.out.println("== processDateTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); @@ -796,6 +827,18 @@ public void processDateTypeProperty(CodegenModel model, CodegenProperty property vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + columnDefinition.put("colNameQuoted", true); + } else { + columnDefinition.put("colNameQuoted", false); + } + columnDefinition.put("tblName", model.getName()); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblNameQuoted", true); + } else { + columnDefinition.put("tblNameQuoted", false); + } + columnDefinition.put("colDataType", dataType); if (Boolean.TRUE.equals(required)) { @@ -826,7 +869,6 @@ public void processDateTypeProperty(CodegenModel model, CodegenProperty property * @param property model's property */ public void processJsonTypeProperty(CodegenModel model, CodegenProperty property) { - System.out.println("== processJsonTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); @@ -853,9 +895,26 @@ public void processJsonTypeProperty(CodegenModel model, CodegenProperty property vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + columnDefinition.put("colNameQuoted", true); + } else { + columnDefinition.put("colNameQuoted", false); + } + columnDefinition.put("tblName", model.getName()); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblNameQuoted", true); + } else { + columnDefinition.put("tblNameQuoted", false); + } + columnDefinition.put("colDataType", dataType); - if (Boolean.FALSE.equals(getJsonDataTypeEnabled())) { + + if (getJsonDataType().equals("off")) { columnDefinition.put("colDataType", "TEXT"); + } else if (getJsonDataType().equals("json")) { + columnDefinition.put("colDataType", "JSON"); + } else if (getJsonDataType().equals("jsonb")) { + columnDefinition.put("colDataType", "JSONB"); } if (Boolean.TRUE.equals(required)) { @@ -887,7 +946,6 @@ public void processJsonTypeProperty(CodegenModel model, CodegenProperty property * @param property model's property */ public void processUnknownTypeProperty(CodegenModel model, CodegenProperty property) { - System.out.println("== processUnknownTypeProperty 1 ============================= "); Map vendorExtensions = property.getVendorExtensions(); Map postgresqlSchema = new HashMap<>(); Map columnDefinition = new HashMap<>(); @@ -913,6 +971,18 @@ public void processUnknownTypeProperty(CodegenModel model, CodegenProperty prope vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + columnDefinition.put("colNameQuoted", true); + } else { + columnDefinition.put("colNameQuoted", false); + } + columnDefinition.put("tblName", model.getName()); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblNameQuoted", true); + } else { + columnDefinition.put("tblNameQuoted", false); + } + columnDefinition.put("colDataType", "TEXT"); if (Boolean.TRUE.equals(required)) { @@ -942,7 +1012,6 @@ public void processUnknownTypeProperty(CodegenModel model, CodegenProperty prope * @return generated codegen property */ public HashMap toCodegenPostgresqlDataTypeArgument(Object value) { - System.out.println("== toCodegenPostgresqlDataTypeArgument 1 ============================= "); HashMap arg = new HashMap<>(); if (value instanceof String) { arg.put("isString", true); @@ -976,7 +1045,6 @@ public HashMap toCodegenPostgresqlDataTypeArgument(Object value) * @return generated codegen property */ public HashMap toCodegenPostgresqlDataTypeDefault(String defaultValue, String postgresqlDataType) { - System.out.println("== toCodegenPostgresqlDataTypeDefault 1 ============================= "); HashMap defaultMap = new HashMap<>(); if (defaultValue == null || defaultValue.toUpperCase(Locale.ROOT).equals("NULL")) { defaultMap.put("defaultValue", "NULL"); @@ -987,9 +1055,7 @@ public HashMap toCodegenPostgresqlDataTypeDefault(String default } switch (postgresqlDataType.toUpperCase(Locale.ROOT)) { - case "TINYINT": case "SMALLINT": - case "MEDIUMINT": case "INT": case "BIGINT": // SERIAL DEFAULT VALUE is a special case. In the definition of an integer @@ -1009,15 +1075,21 @@ public HashMap toCodegenPostgresqlDataTypeDefault(String default } return defaultMap; case "TIMESTAMP": - case "DATETIME": - // The exception is that, for TIMESTAMP and DATETIME columns, you can specify - // CURRENT_TIMESTAMP as the default + case "DATE": + // The exception is that, for TIMESTAMP,DATE columns, you can specify + // CURRENT_TIMESTAMP,CURRENT_DATE as the default if (defaultValue.equals("CURRENT_TIMESTAMP")) { defaultMap.put("defaultValue", defaultValue); defaultMap.put("isString", false); defaultMap.put("isNumeric", false); defaultMap.put("isKeyword", true); + } else if (defaultValue.equals("CURRENT_DATE")) { + defaultMap.put("defaultValue", defaultValue); + defaultMap.put("isString", false); + defaultMap.put("isNumeric", false); + defaultMap.put("isKeyword", true); + } else { defaultMap.put("defaultValue", defaultValue); defaultMap.put("isString", true); @@ -1026,16 +1098,11 @@ public HashMap toCodegenPostgresqlDataTypeDefault(String default } return defaultMap; - case "TINYBLOB": - case "BLOB": - case "MEDIUMBLOB": - case "LONGBLOB": - case "TINYTEXT": + case "BYTEA": case "TEXT": - case "MEDIUMTEXT": - case "LONGTEXT": case "GEOMETRY": case "JSON": + case "JSONB": // The BLOB, TEXT, GEOMETRY, and JSON data types cannot be assigned a default // value. throw new RuntimeException( @@ -1060,7 +1127,6 @@ public HashMap toCodegenPostgresqlDataTypeDefault(String default * @return PostgreSQL integer data type */ public String getPostgresqlMatchedIntegerDataType(Long minimum, Long maximum, Boolean unsigned) { - System.out.println("== getPostgresqlMatchedIntegerDataType 1 ============================= "); // we can choose fit postgresql data type // ref: https://www.postgresql.org/docs/17/datatype-numeric.html long min = (minimum != null) ? minimum : -2147483648L; @@ -1089,7 +1155,6 @@ public String getPostgresqlMatchedIntegerDataType(Long minimum, Long maximum, Bo * @return PostgreSQL string data type */ public String getPostgresqlMatchedStringDataType(Integer minLength, Integer maxLength) { - System.out.println("== getPostgresqlMatchedStringDataType 1 ============================= "); // we can choose fit postgresql data type // ref: https://www.postgresql.org/docs/17/datatype-character.html int min = (minLength != null && minLength >= 0) ? minLength : 0; @@ -1113,12 +1178,12 @@ public String getPostgresqlMatchedStringDataType(Integer minLength, Integer maxL * @return true if value is correct PostgreSQL data type, otherwise false */ public Boolean isPostgresqlDataType(String dataType) { - System.out.println("== isPostgresqlDataType 1 ============================= "); return (postgresqlNumericTypes.contains(dataType.toUpperCase(Locale.ROOT)) || postgresqlDateAndTimeTypes.contains(dataType.toUpperCase(Locale.ROOT)) || postgresqlStringTypes.contains(dataType.toUpperCase(Locale.ROOT)) || - postgresqlSpatialTypes.contains(dataType.toUpperCase(Locale.ROOT))); - // || dataType.toUpperCase(Locale.ROOT).equals("JSON")); + postgresqlSpatialTypes.contains(dataType.toUpperCase(Locale.ROOT))) || + dataType.toUpperCase(Locale.ROOT).equals("JSON") || + dataType.toUpperCase(Locale.ROOT).equals("JSONB"); } /** @@ -1256,9 +1321,9 @@ public String escapePostgresqlQuotedIdentifier(String identifier) { @Override public String escapeReservedWord(String name) { - LOGGER.warn( - "'{}' is PostgreSQL reserved word. Do not use that word or properly escape it with backticks in mustache template", - name); + // LOGGER.warn( + // "'{}' is PostgreSQL reserved word. Do not use that word or properly escape it with backticks in mustache template", + // name); return name; } diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/README.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/README.mustache index aee9538f4cd0..744281057fee 100644 --- a/modules/openapi-generator/src/main/resources/postgresql-schema/README.mustache +++ b/modules/openapi-generator/src/main/resources/postgresql-schema/README.mustache @@ -1,56 +1,48 @@ # PostgreSQL Schema Codegen -Main goal of this generator is to provide database structure file almost identical you usually generate with: -- PHPMyAdmin (Export structure only, SQL syntax) -- Adminer -- `postgresqldump` function +Main goal of this generator is to provide PostgreSQL database DDL script that drops and then creates database objects for the given OpenAPI application [PostgreSQL documentation](https://dev.postgresql.com/doc/) ## Requirements -- PostgreSQL Server ^5.7.8 (`JSON` column type added) +- PostgreSQL Server v9.4 or newer -## Openapi Data Type to PostgreSQL Data Type mapping +## OpenAPI Data Type to PostgreSQL data type mapping -| Openapi Data Type | Openapi Data Format | Dependent properties | PostgreSQL Data Types | Default PostgreSQL Data Type | +| OpenAPI data type | OpenAPI data format | Dependent properties | PostgreSQL data types | Default PostgreSQL data type | | --- | --- | --- | --- | --- | -| `integer` | `int32` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `TINYINT` / `SMALLINT` / `MEDIUMINT`/ `INT` / `BIGINT` | `INT` | -| `integer` | `int64` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `TINYINT` / `SMALLINT` / `MEDIUMINT` / `INT` / `BIGINT` | `BIGINT` | -| `boolean` | | | `TINYINT` | `TINYINT` | +| `integer` | `int32` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `SMALLINT` / `INT` / `BIGINT` | `INT` | +| `integer` | `int64` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `SMALLINT` / `INT` / `BIGINT` | `BIGINT` | +| `boolean` | | | `BOOLEAN` | `BOOLEAN` | | `number` | `float` | | `DECIMAL` | `DECIMAL` | | `number` | `double` | | `DECIMAL` | `DECIMAL` | -| `string` | | `minLength` / `maxLength` | `CHAR` / `VARCHAR` / `TEXT` / `MEDIUMTEXT` / `LONGTEXT` | `TEXT` | -| `string` | `byte` | | `TEXT` | `TEXT` | -| `string` | `binary` | | `MEDIUMBLOB` | `MEDIUMBLOB` | -| `file` | | | `MEDIUMBLOB` | `MEDIUMBLOB` | +| `string` | | `minLength` / `maxLength` | `VARCHAR` / `TEXT` | `TEXT` | +| `string` | `byte` | | `BYTEA` | `BYTEA` | +| `string` | `binary` | | `BYTEA` | `BYTEA` | +| `file` | | | `BYTEA` | `BYTEA` | | `string` | `date` | | `DATE` | `DATE` | -| `string` | `date-time` | | `DATETIME` | `DATETIME` | -| `string` | `enum` | | `ENUM` | `ENUM` | -| `array` | | | `JSON` | `JSON` | -| `object` | | | `JSON` | `JSON` | +| `string` | `date-time` | | `TIMESTAMP` | `TIMESTAMP` | +| `string` | `enum` | | `ENUM`
    (via separate ENUM data type) | `ENUM`
    (via separate ENUM data type) | +| `array` | | | `JSON` / `JSONB` / `TEXT` | `JSON` | +| `object` | | | `JSON` / `JSONB` / `TEXT` | `JSON` | | `\Model\User` (referenced definition) | | | `TEXT` | `TEXT` | ## How to use -Produced file(`postgresql_schema.sql`) contains every table definition. Current implementation doesn't drop or modify existed tables, if you want rewrite whole schema make sure they're not presented. +Produced files: -### PHPMyAdmin +1. `postgresql_schema.sql` that contains: + + - `DROP ...` SQL statements for dropping every table and data type generated by this script; -1. Choose **Import** tab from the home screen -2. In section **File to import** click to **Choose File** and find generated `postgresql_schema.sql` -3. Make sure **Format** selector set to **SQL** -4. Push **Go** button + - `CREATE ...` SQL statements for creating every table and data types for them (for `ENUM` types). -### Adminer + *Note: For safety reasons `DROP ...` SQL statements are commented out by default. Uncomment them before use.* -1. Click **Import** link in left sidebar -2. In **File upload** fieldset click to **Choose Files** and find generated `postgresql_schema.sql` -3. Push **Execute** button + *Note: `ENUM` data types are implemented by creating separate data types first using command `CREATE TYPE` (one data type for each `ENUM` column). Then created data type is used as data type for table column.* -### Prepared SQL queries +2. `postgresql_schema_oauth2.sql` that contains table creation commands for Oauth2-related tables. + +3. [Model folder]({{modelSrcPath}}) contains files with sample SQL queries for each table. Copy-paste them, edit and use. -[Model folder]({{modelSrcPath}}) contains SQL queries(`SELECT *`, `SELECT`, `INSERT`, `UPDATE`, `DELETE`) usually suggested by `PHPMyAdmin` when you hit `SQL` tab. They are absolutely useless without adaptation to your needs. Copy-paste them then edit. - -Important! Some of SQLs(`INSERT`/`UPDATE`) contains {{#namedParametersEnabled}}named parameters eg. :namedParam{{/namedParametersEnabled}}{{^namedParametersEnabled}}question marks(`?`) which are parameter placeholders{{/namedParametersEnabled}}. You need to bind values to these params to execute query. - -If your PostgreSQL driver doesn't support named parameters(`PHP PDO` supports while `PHP postgresqli` doesn't) you need to make sure that `namedParametersEnabled` generator option is disabled. +*Note: Important! Some of SQLs(`INSERT`/`UPDATE`) contains {{#namedParametersEnabled}}named parameters eg. :namedParam{{/namedParametersEnabled}}{{^namedParametersEnabled}}question marks(`?`) which are parameter placeholders{{/namedParametersEnabled}}. You need to bind values to these params to execute query.* diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache index a95746e3a5ca..26dcf1c4e472 100644 --- a/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache +++ b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache @@ -1,8 +1,6 @@ -- -- Schema objects for PostgreSQL -{{#defaultDatabaseName}} --- Database: {{{.}}} -{{/defaultDatabaseName}} +-- "{{appName}}" -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) {{#currDateTime}} @@ -17,7 +15,7 @@ -- -- TABLES -- -{{#models}}{{#model}}{{#hasVars}}{{^isArray}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}-- DROP TABLE IF EXISTS {{#defaultDatabaseName}}{{{.}}}.{{/defaultDatabaseName}}{{tblName}}; +{{#models}}{{#model}}{{#hasVars}}{{^isArray}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}-- DROP TABLE IF EXISTS {{#defaultDatabaseName}}{{{.}}}.{{/defaultDatabaseName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}{{tblName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}; {{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/isArray}}{{/hasVars}}{{/model}}{{/models}} -- -- TYPES @@ -25,7 +23,6 @@ {{#models}}{{#model}}{{#hasVars}}{{^isArray}}{{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#typeDefinition}}-- DROP TYPE IF EXISTS {{typeName}}; {{/typeDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}}{{/isArray}}{{/hasVars}}{{/model}}{{/models}} - -- -- CREATE OBJECTS -- @@ -33,11 +30,9 @@ -- {{#models}}{{#model}}{{#hasVars}}{{^isArray}}{{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#typeDefinition}}CREATE TYPE {{typeName}} AS ENUM{{#typeArguments}}{{#-first}}({{/-first}}{{#isString}}'{{/isString}}{{argumentValue}}{{#isString}}'{{/isString}}{{^-last}}, {{/-last}}{{#-last}}); {{/-last}}{{/typeArguments}}{{/typeDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}}{{/isArray}}{{/hasVars}}{{/model}}{{/models}} - -- -- TABLES -- - {{#models}}{{#model}}{{#hasVars}}{{^isArray}}-- -- Table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}'{{tblName}}'{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} generated from model '{{classVarName}}' {{#description}} @@ -47,7 +42,7 @@ {{#vendorExtensions}} {{#x-postgresql-schema}} {{#tableDefinition}} -CREATE TABLE IF NOT EXISTS {{#defaultDatabaseName}}{{{.}}}.{{/defaultDatabaseName}}{{tblName}} ( +CREATE TABLE IF NOT EXISTS {{#defaultDatabaseName}}{{{.}}}.{{/defaultDatabaseName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}{{tblName}}{{#tblNameQuoted}}"{{/tblNameQuoted}} ( {{/tableDefinition}} {{/x-postgresql-schema}} {{/vendorExtensions}} @@ -55,7 +50,7 @@ CREATE TABLE IF NOT EXISTS {{#defaultDatabaseName}}{{{.}}}.{{/defaultDatabaseNam {{#vendorExtensions}} {{#x-postgresql-schema}} {{#columnDefinition}} - {{colName}} {{colDataType}}{{#colNotNull}} NOT NULL{{/colNotNull}}{{#colDefault}} DEFAULT {{#isString}}'{{defaultValue}}'{{/isString}}{{^isString}}{{defaultValue}}{{/isString}}{{/colDefault}}{{^-last}},{{/-last}} + {{#colNameQuoted}}"{{/colNameQuoted}}{{colName}}{{#colNameQuoted}}"{{/colNameQuoted}} {{colDataType}}{{#colDataTypeArguments}}{{#-first}}({{/-first}}{{#isString}}'{{/isString}}{{argumentValue}}{{#isString}}'{{/isString}}{{^-last}}, {{/-last}}{{#-last}}){{/-last}}{{/colDataTypeArguments}}{{#colNotNull}} NOT NULL{{/colNotNull}}{{#colDefault}} DEFAULT {{#isString}}'{{defaultValue}}'{{/isString}}{{^isString}}{{defaultValue}}{{/isString}}{{/colDefault}}{{^-last}},{{/-last}} {{/columnDefinition}} {{/x-postgresql-schema}} {{/vendorExtensions}} @@ -64,9 +59,12 @@ CREATE TABLE IF NOT EXISTS {{#defaultDatabaseName}}{{{.}}}.{{/defaultDatabaseNam {{#x-postgresql-schema}} {{#tableDefinition}} ); - {{/tableDefinition}} {{/x-postgresql-schema}} {{/vendorExtensions}} -{{/isArray}}{{/hasVars}}{{/model}}{{/models}} - +{{#vendorExtensions}} +{{#x-postgresql-schema}}{{#tableDefinition}}{{#tblComment}}COMMENT ON TABLE {{#tblNameQuoted}}"{{/tblNameQuoted}}{{tblName}}{{#tblNameQuoted}}"{{/tblNameQuoted}} IS '{{.}}'{{/tblComment}}; +{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}{{#colComment}}COMMENT ON COLUMN {{#tblNameQuoted}}"{{/tblNameQuoted}}{{tblName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}.{{#colNameQuoted}}"{{/colNameQuoted}}{{colName}}{{#colNameQuoted}}"{{/colNameQuoted}} IS '{{.}}'; +{{/colComment}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}}{{#vendorExtensions}} +{{/vendorExtensions}} +{{/isArray}}{{/hasVars}}{{/model}}{{/models}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/query_examples.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/query_examples.mustache new file mode 100644 index 000000000000..3101b7d6d595 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/postgresql-schema/query_examples.mustache @@ -0,0 +1,32 @@ +-- +-- "{{appName}}" +-- Prepared SQL queries for {{#models}}{{#model}}'{{{name}}}'{{/model}}{{/models}} definition. +-- Created using 'openapi-generator' ('postgresql-schema' generator) +-- (https://openapi-generator.tech/docs/generators/postgresql-schema) +{{#currDateTime}} +-- +-- Created: {{{.}}} +{{/currDateTime}} +-- + +{{#models}}{{#model}} +-- +-- SELECT template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}'{{tblName}}'{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} +-- +SELECT {{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}{{#colNameQuoted}}"{{/colNameQuoted}}{{colName}}{{#colNameQuoted}}"{{/colNameQuoted}}{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}} FROM {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}{{{.}}}.{{/defaultDatabaseName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}{{tblName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} WHERE 1=1; + +-- +-- INSERT template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}'{{tblName}}'{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} +-- +INSERT INTO {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}{{{.}}}.{{/defaultDatabaseName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}{{tblName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} ({{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}{{#colNameQuoted}}"{{/colNameQuoted}}{{colName}}{{#colNameQuoted}}"{{/colNameQuoted}}{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}}) VALUES ({{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}{{#namedParametersEnabled}}:{{colName}}{{/namedParametersEnabled}}{{^namedParametersEnabled}}?{{/namedParametersEnabled}}{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}}); + +-- +-- UPDATE template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}'{{tblName}}'{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} +-- +UPDATE {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}{{tblName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} SET {{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}{{#colNameQuoted}}"{{/colNameQuoted}}{{colName}}{{#colNameQuoted}}"{{/colNameQuoted}} = {{#namedParametersEnabled}}:{{colName}}{{/namedParametersEnabled}}{{^namedParametersEnabled}}?{{/namedParametersEnabled}}{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}} WHERE 1=2; + +-- +-- DELETE template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}'{{tblName}}'{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} +-- +DELETE FROM {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}{{tblName}}{{#tblNameQuoted}}"{{/tblNameQuoted}}{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} WHERE 1=2; +{{/model}}{{/models}} diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/sql_query.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/sql_query.mustache deleted file mode 100644 index 054b0baecfc3..000000000000 --- a/modules/openapi-generator/src/main/resources/postgresql-schema/sql_query.mustache +++ /dev/null @@ -1,27 +0,0 @@ --- --- {{appName}}.{{#defaultDatabaseName}} --- Database: `{{{.}}}`{{/defaultDatabaseName}} --- Prepared SQL queries for {{#models}}{{#model}}'{{{name}}}'{{/model}}{{/models}} definition. --- - -{{#models}}{{#model}} --- --- SELECT template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} --- -SELECT {{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}`{{colName}}`{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}} FROM {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} WHERE 1; - --- --- INSERT template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} --- -INSERT INTO {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}({{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}`{{colName}}`{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}}) VALUES ({{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}{{#namedParametersEnabled}}:{{colName}}{{/namedParametersEnabled}}{{^namedParametersEnabled}}?{{/namedParametersEnabled}}{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}}); - --- --- UPDATE template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} --- -UPDATE {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} SET {{#vars}}{{#vendorExtensions}}{{#x-postgresql-schema}}{{#columnDefinition}}`{{colName}}` = {{#namedParametersEnabled}}:{{colName}}{{/namedParametersEnabled}}{{^namedParametersEnabled}}?{{/namedParametersEnabled}}{{^-last}}, {{/-last}}{{/columnDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}}{{/vars}} WHERE 1; - --- --- DELETE template for table {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} --- -DELETE FROM {{#vendorExtensions}}{{#x-postgresql-schema}}{{#tableDefinition}}{{#defaultDatabaseName}}`{{{.}}}`.{{/defaultDatabaseName}}`{{tblName}}`{{/tableDefinition}}{{/x-postgresql-schema}}{{/vendorExtensions}} WHERE 0; -{{/model}}{{/models}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/PostgresqlSchemaOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/PostgresqlSchemaOptionsProvider.java index 7e71c334342c..d84e43f003b2 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/PostgresqlSchemaOptionsProvider.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/PostgresqlSchemaOptionsProvider.java @@ -23,9 +23,10 @@ public class PostgresqlSchemaOptionsProvider implements OptionsProvider { public static final String DEFAULT_DATABASE_NAME_VALUE = "database_name"; - public static final String JSON_DATA_TYPE_ENABLED_VALUE = "false"; + public static final String JSON_DATA_TYPE_VALUE = "json"; public static final String IDENTIFIER_NAMING_CONVENTION_VALUE = "snake_case"; public static final String NAMED_PARAMETERS_ENABLED_VALUE = "true"; + public static final String ID_AUTOINC_ENABLED_VALUE = "false"; @Override public String getLanguage() { @@ -36,9 +37,10 @@ public String getLanguage() { public Map createOptions() { ImmutableMap.Builder builder = new ImmutableMap.Builder(); return builder.put(PostgresqlSchemaCodegen.DEFAULT_DATABASE_NAME, DEFAULT_DATABASE_NAME_VALUE) - .put(PostgresqlSchemaCodegen.JSON_DATA_TYPE_ENABLED, JSON_DATA_TYPE_ENABLED_VALUE) + .put(PostgresqlSchemaCodegen.JSON_DATA_TYPE, JSON_DATA_TYPE_VALUE) .put(PostgresqlSchemaCodegen.IDENTIFIER_NAMING_CONVENTION, IDENTIFIER_NAMING_CONVENTION_VALUE) .put(PostgresqlSchemaCodegen.NAMED_PARAMETERS_ENABLED, NAMED_PARAMETERS_ENABLED_VALUE) + .put(PostgresqlSchemaCodegen.ID_AUTOINC_ENABLED, ID_AUTOINC_ENABLED_VALUE) .build(); } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java index 857efee5ac71..db7ca5fa3701 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java @@ -255,20 +255,22 @@ public void testGetDefaultDatabaseName() { } @Test - public void testSetJsonDataTypeEnabled() { + public void testSetJsonDataType() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); - codegen.setJsonDataTypeEnabled(true); - Assert.assertTrue(codegen.getJsonDataTypeEnabled()); - codegen.setJsonDataTypeEnabled(false); - Assert.assertFalse(codegen.getJsonDataTypeEnabled()); + codegen.setJsonDataType("off"); + Assert.assertTrue(codegen.getJsonDataType().equals("off")); + codegen.setJsonDataType("json"); + Assert.assertTrue(codegen.getJsonDataType().equals("json")); + codegen.setJsonDataType("jsonb"); + Assert.assertTrue(codegen.getJsonDataType().equals("jsonb")); } @Test - public void testGetJsonDataTypeEnabled() { + public void testGetJsonDataType() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); - Assert.assertTrue(codegen.getJsonDataTypeEnabled()); - codegen.setJsonDataTypeEnabled(false); - Assert.assertFalse(codegen.getJsonDataTypeEnabled()); + Assert.assertTrue(codegen.getJsonDataType().equals("json")); + codegen.setJsonDataType("off"); + Assert.assertTrue(codegen.getJsonDataType().equals("off")); } @Test diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java index ee02677ad970..d3582a69d283 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java @@ -40,7 +40,7 @@ protected CodegenConfig getCodegenConfig() { @Override protected void verifyOptions() { verify(clientCodegen).setDefaultDatabaseName(PostgresqlSchemaOptionsProvider.DEFAULT_DATABASE_NAME_VALUE); - verify(clientCodegen).setJsonDataTypeEnabled(Boolean.valueOf(PostgresqlSchemaOptionsProvider.JSON_DATA_TYPE_ENABLED_VALUE)); + verify(clientCodegen).setJsonDataType(PostgresqlSchemaOptionsProvider.JSON_DATA_TYPE_VALUE); verify(clientCodegen).setIdentifierNamingConvention(PostgresqlSchemaOptionsProvider.IDENTIFIER_NAMING_CONVENTION_VALUE); verify(clientCodegen).setNamedParametersEnabled(Boolean.valueOf(PostgresqlSchemaOptionsProvider.NAMED_PARAMETERS_ENABLED_VALUE)); } From a592000ece46fb4fb6b0b1ef281e2a2039a9b22e Mon Sep 17 00:00:00 2001 From: Ingars Ribners Date: Thu, 5 Dec 2024 01:29:34 +0300 Subject: [PATCH 4/9] Samples for 'postgresql-schema' updated. --- .../postgresql/.openapi-generator/FILES | 1 + .../petstore/postgresql/Model/ApiResponse.sql | 22 +- .../petstore/postgresql/Model/Category.sql | 22 +- .../petstore/postgresql/Model/Order.sql | 22 +- .../schema/petstore/postgresql/Model/Pet.sql | 22 +- .../schema/petstore/postgresql/Model/Tag.sql | 22 +- .../schema/petstore/postgresql/Model/User.sql | 22 +- samples/schema/petstore/postgresql/README.md | 60 ++--- .../petstore/postgresql/postgresql_schema.sql | 238 ++++++------------ .../postgresql/postgresql_schema_oauth2.sql | 121 +++++++++ 10 files changed, 309 insertions(+), 243 deletions(-) create mode 100644 samples/schema/petstore/postgresql/postgresql_schema_oauth2.sql diff --git a/samples/schema/petstore/postgresql/.openapi-generator/FILES b/samples/schema/petstore/postgresql/.openapi-generator/FILES index 61c5bf401036..6e3acfba2157 100644 --- a/samples/schema/petstore/postgresql/.openapi-generator/FILES +++ b/samples/schema/petstore/postgresql/.openapi-generator/FILES @@ -6,3 +6,4 @@ Model/Tag.sql Model/User.sql README.md postgresql_schema.sql +postgresql_schema_oauth2.sql diff --git a/samples/schema/petstore/postgresql/Model/ApiResponse.sql b/samples/schema/petstore/postgresql/Model/ApiResponse.sql index 8aac784b7f89..5ec144897f3a 100644 --- a/samples/schema/petstore/postgresql/Model/ApiResponse.sql +++ b/samples/schema/petstore/postgresql/Model/ApiResponse.sql @@ -1,26 +1,30 @@ -- --- OpenAPI Petstore. +-- "OpenAPI Petstore" -- Prepared SQL queries for 'ApiResponse' definition. +-- Created using 'openapi-generator' ('postgresql-schema' generator) +-- (https://openapi-generator.tech/docs/generators/postgresql-schema) +-- +-- Created: 04.12.2024 22:27:11 -- -- --- SELECT template for table `ApiResponse` +-- SELECT template for table 'ApiResponse' -- -SELECT `code`, `type`, `message` FROM `ApiResponse` WHERE 1; +SELECT code, "type", message FROM ApiResponse WHERE 1=1; -- --- INSERT template for table `ApiResponse` +-- INSERT template for table 'ApiResponse' -- -INSERT INTO `ApiResponse`(`code`, `type`, `message`) VALUES (?, ?, ?); +INSERT INTO ApiResponse (code, "type", message) VALUES (?, ?, ?); -- --- UPDATE template for table `ApiResponse` +-- UPDATE template for table 'ApiResponse' -- -UPDATE `ApiResponse` SET `code` = ?, `type` = ?, `message` = ? WHERE 1; +UPDATE ApiResponse SET code = ?, "type" = ?, message = ? WHERE 1=2; -- --- DELETE template for table `ApiResponse` +-- DELETE template for table 'ApiResponse' -- -DELETE FROM `ApiResponse` WHERE 0; +DELETE FROM ApiResponse WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/Model/Category.sql b/samples/schema/petstore/postgresql/Model/Category.sql index f7d7166644d2..03d8eaacc76e 100644 --- a/samples/schema/petstore/postgresql/Model/Category.sql +++ b/samples/schema/petstore/postgresql/Model/Category.sql @@ -1,26 +1,30 @@ -- --- OpenAPI Petstore. +-- "OpenAPI Petstore" -- Prepared SQL queries for 'Category' definition. +-- Created using 'openapi-generator' ('postgresql-schema' generator) +-- (https://openapi-generator.tech/docs/generators/postgresql-schema) +-- +-- Created: 04.12.2024 22:27:11 -- -- --- SELECT template for table `Category` +-- SELECT template for table 'Category' -- -SELECT `id`, `name` FROM `Category` WHERE 1; +SELECT id, "name" FROM Category WHERE 1=1; -- --- INSERT template for table `Category` +-- INSERT template for table 'Category' -- -INSERT INTO `Category`(`id`, `name`) VALUES (?, ?); +INSERT INTO Category (id, "name") VALUES (?, ?); -- --- UPDATE template for table `Category` +-- UPDATE template for table 'Category' -- -UPDATE `Category` SET `id` = ?, `name` = ? WHERE 1; +UPDATE Category SET id = ?, "name" = ? WHERE 1=2; -- --- DELETE template for table `Category` +-- DELETE template for table 'Category' -- -DELETE FROM `Category` WHERE 0; +DELETE FROM Category WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/Model/Order.sql b/samples/schema/petstore/postgresql/Model/Order.sql index 43af471ffb5c..b8f1003f3bc8 100644 --- a/samples/schema/petstore/postgresql/Model/Order.sql +++ b/samples/schema/petstore/postgresql/Model/Order.sql @@ -1,26 +1,30 @@ -- --- OpenAPI Petstore. +-- "OpenAPI Petstore" -- Prepared SQL queries for 'Order' definition. +-- Created using 'openapi-generator' ('postgresql-schema' generator) +-- (https://openapi-generator.tech/docs/generators/postgresql-schema) +-- +-- Created: 04.12.2024 22:27:11 -- -- --- SELECT template for table `Order` +-- SELECT template for table 'Order' -- -SELECT `id`, `petId`, `quantity`, `shipDate`, `status`, `complete` FROM `Order` WHERE 1; +SELECT id, petId, quantity, shipDate, status, complete FROM "Order" WHERE 1=1; -- --- INSERT template for table `Order` +-- INSERT template for table 'Order' -- -INSERT INTO `Order`(`id`, `petId`, `quantity`, `shipDate`, `status`, `complete`) VALUES (?, ?, ?, ?, ?, ?); +INSERT INTO "Order" (id, petId, quantity, shipDate, status, complete) VALUES (?, ?, ?, ?, ?, ?); -- --- UPDATE template for table `Order` +-- UPDATE template for table 'Order' -- -UPDATE `Order` SET `id` = ?, `petId` = ?, `quantity` = ?, `shipDate` = ?, `status` = ?, `complete` = ? WHERE 1; +UPDATE "Order" SET id = ?, petId = ?, quantity = ?, shipDate = ?, status = ?, complete = ? WHERE 1=2; -- --- DELETE template for table `Order` +-- DELETE template for table 'Order' -- -DELETE FROM `Order` WHERE 0; +DELETE FROM "Order" WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/Model/Pet.sql b/samples/schema/petstore/postgresql/Model/Pet.sql index b4c6a135c038..f365dd9ca2b2 100644 --- a/samples/schema/petstore/postgresql/Model/Pet.sql +++ b/samples/schema/petstore/postgresql/Model/Pet.sql @@ -1,26 +1,30 @@ -- --- OpenAPI Petstore. +-- "OpenAPI Petstore" -- Prepared SQL queries for 'Pet' definition. +-- Created using 'openapi-generator' ('postgresql-schema' generator) +-- (https://openapi-generator.tech/docs/generators/postgresql-schema) +-- +-- Created: 04.12.2024 22:27:11 -- -- --- SELECT template for table `Pet` +-- SELECT template for table 'Pet' -- -SELECT `id`, `category`, `name`, `photoUrls`, `tags`, `status` FROM `Pet` WHERE 1; +SELECT id, category, "name", photoUrls, tags, status FROM Pet WHERE 1=1; -- --- INSERT template for table `Pet` +-- INSERT template for table 'Pet' -- -INSERT INTO `Pet`(`id`, `category`, `name`, `photoUrls`, `tags`, `status`) VALUES (?, ?, ?, ?, ?, ?); +INSERT INTO Pet (id, category, "name", photoUrls, tags, status) VALUES (?, ?, ?, ?, ?, ?); -- --- UPDATE template for table `Pet` +-- UPDATE template for table 'Pet' -- -UPDATE `Pet` SET `id` = ?, `category` = ?, `name` = ?, `photoUrls` = ?, `tags` = ?, `status` = ? WHERE 1; +UPDATE Pet SET id = ?, category = ?, "name" = ?, photoUrls = ?, tags = ?, status = ? WHERE 1=2; -- --- DELETE template for table `Pet` +-- DELETE template for table 'Pet' -- -DELETE FROM `Pet` WHERE 0; +DELETE FROM Pet WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/Model/Tag.sql b/samples/schema/petstore/postgresql/Model/Tag.sql index d62633f8427e..324d1851a098 100644 --- a/samples/schema/petstore/postgresql/Model/Tag.sql +++ b/samples/schema/petstore/postgresql/Model/Tag.sql @@ -1,26 +1,30 @@ -- --- OpenAPI Petstore. +-- "OpenAPI Petstore" -- Prepared SQL queries for 'Tag' definition. +-- Created using 'openapi-generator' ('postgresql-schema' generator) +-- (https://openapi-generator.tech/docs/generators/postgresql-schema) +-- +-- Created: 04.12.2024 22:27:11 -- -- --- SELECT template for table `Tag` +-- SELECT template for table 'Tag' -- -SELECT `id`, `name` FROM `Tag` WHERE 1; +SELECT id, "name" FROM Tag WHERE 1=1; -- --- INSERT template for table `Tag` +-- INSERT template for table 'Tag' -- -INSERT INTO `Tag`(`id`, `name`) VALUES (?, ?); +INSERT INTO Tag (id, "name") VALUES (?, ?); -- --- UPDATE template for table `Tag` +-- UPDATE template for table 'Tag' -- -UPDATE `Tag` SET `id` = ?, `name` = ? WHERE 1; +UPDATE Tag SET id = ?, "name" = ? WHERE 1=2; -- --- DELETE template for table `Tag` +-- DELETE template for table 'Tag' -- -DELETE FROM `Tag` WHERE 0; +DELETE FROM Tag WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/Model/User.sql b/samples/schema/petstore/postgresql/Model/User.sql index 91afbc9e2457..6035b3be0d8b 100644 --- a/samples/schema/petstore/postgresql/Model/User.sql +++ b/samples/schema/petstore/postgresql/Model/User.sql @@ -1,26 +1,30 @@ -- --- OpenAPI Petstore. +-- "OpenAPI Petstore" -- Prepared SQL queries for 'User' definition. +-- Created using 'openapi-generator' ('postgresql-schema' generator) +-- (https://openapi-generator.tech/docs/generators/postgresql-schema) +-- +-- Created: 04.12.2024 22:27:11 -- -- --- SELECT template for table `User` +-- SELECT template for table 'User' -- -SELECT `id`, `username`, `firstName`, `lastName`, `email`, `password`, `phone`, `userStatus` FROM `User` WHERE 1; +SELECT id, username, firstName, lastName, email, "password", phone, userStatus FROM "User" WHERE 1=1; -- --- INSERT template for table `User` +-- INSERT template for table 'User' -- -INSERT INTO `User`(`id`, `username`, `firstName`, `lastName`, `email`, `password`, `phone`, `userStatus`) VALUES (?, ?, ?, ?, ?, ?, ?, ?); +INSERT INTO "User" (id, username, firstName, lastName, email, "password", phone, userStatus) VALUES (?, ?, ?, ?, ?, ?, ?, ?); -- --- UPDATE template for table `User` +-- UPDATE template for table 'User' -- -UPDATE `User` SET `id` = ?, `username` = ?, `firstName` = ?, `lastName` = ?, `email` = ?, `password` = ?, `phone` = ?, `userStatus` = ? WHERE 1; +UPDATE "User" SET id = ?, username = ?, firstName = ?, lastName = ?, email = ?, "password" = ?, phone = ?, userStatus = ? WHERE 1=2; -- --- DELETE template for table `User` +-- DELETE template for table 'User' -- -DELETE FROM `User` WHERE 0; +DELETE FROM "User" WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/README.md b/samples/schema/petstore/postgresql/README.md index 0e0d7d5887ff..2532c72fba0f 100644 --- a/samples/schema/petstore/postgresql/README.md +++ b/samples/schema/petstore/postgresql/README.md @@ -1,56 +1,48 @@ # PostgreSQL Schema Codegen -Main goal of this generator is to provide database structure file almost identical you usually generate with: -- PHPMyAdmin (Export structure only, SQL syntax) -- Adminer -- `postgresqldump` function +Main goal of this generator is to provide PostgreSQL database DDL script that drops and then creates database objects for the given OpenAPI application [PostgreSQL documentation](https://dev.postgresql.com/doc/) ## Requirements -- PostgreSQL Server ^5.7.8 (`JSON` column type added) +- PostgreSQL Server v9.4 or newer -## Openapi Data Type to PostgreSQL Data Type mapping +## OpenAPI Data Type to PostgreSQL data type mapping -| Openapi Data Type | Openapi Data Format | Dependent properties | PostgreSQL Data Types | Default PostgreSQL Data Type | +| OpenAPI data type | OpenAPI data format | Dependent properties | PostgreSQL data types | Default PostgreSQL data type | | --- | --- | --- | --- | --- | -| `integer` | `int32` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `TINYINT` / `SMALLINT` / `MEDIUMINT`/ `INT` / `BIGINT` | `INT` | -| `integer` | `int64` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `TINYINT` / `SMALLINT` / `MEDIUMINT` / `INT` / `BIGINT` | `BIGINT` | -| `boolean` | | | `TINYINT` | `TINYINT` | +| `integer` | `int32` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `SMALLINT` / `INT` / `BIGINT` | `INT` | +| `integer` | `int64` | `minimum` / `maximum` / `minimumExclusive` / `maximumExclusive` | `SMALLINT` / `INT` / `BIGINT` | `BIGINT` | +| `boolean` | | | `BOOLEAN` | `BOOLEAN` | | `number` | `float` | | `DECIMAL` | `DECIMAL` | | `number` | `double` | | `DECIMAL` | `DECIMAL` | -| `string` | | `minLength` / `maxLength` | `CHAR` / `VARCHAR` / `TEXT` / `MEDIUMTEXT` / `LONGTEXT` | `TEXT` | -| `string` | `byte` | | `TEXT` | `TEXT` | -| `string` | `binary` | | `MEDIUMBLOB` | `MEDIUMBLOB` | -| `file` | | | `MEDIUMBLOB` | `MEDIUMBLOB` | +| `string` | | `minLength` / `maxLength` | `VARCHAR` / `TEXT` | `TEXT` | +| `string` | `byte` | | `BYTEA` | `BYTEA` | +| `string` | `binary` | | `BYTEA` | `BYTEA` | +| `file` | | | `BYTEA` | `BYTEA` | | `string` | `date` | | `DATE` | `DATE` | -| `string` | `date-time` | | `DATETIME` | `DATETIME` | -| `string` | `enum` | | `ENUM` | `ENUM` | -| `array` | | | `JSON` | `JSON` | -| `object` | | | `JSON` | `JSON` | +| `string` | `date-time` | | `TIMESTAMP` | `TIMESTAMP` | +| `string` | `enum` | | `ENUM`
    (via separate ENUM data type) | `ENUM`
    (via separate ENUM data type) | +| `array` | | | `JSON` / `JSONB` / `TEXT` | `JSON` | +| `object` | | | `JSON` / `JSONB` / `TEXT` | `JSON` | | `\Model\User` (referenced definition) | | | `TEXT` | `TEXT` | ## How to use -Produced file(`postgresql_schema.sql`) contains every table definition. Current implementation doesn't drop or modify existed tables, if you want rewrite whole schema make sure they're not presented. +Produced files: -### PHPMyAdmin +1. `postgresql_schema.sql` that contains: + + - `DROP ...` SQL statements for dropping every table and data type generated by this script; -1. Choose **Import** tab from the home screen -2. In section **File to import** click to **Choose File** and find generated `postgresql_schema.sql` -3. Make sure **Format** selector set to **SQL** -4. Push **Go** button + - `CREATE ...` SQL statements for creating every table and data types for them (for `ENUM` types). -### Adminer + *Note: For safety reasons `DROP ...` SQL statements are commented out by default. Uncomment them before use.* -1. Click **Import** link in left sidebar -2. In **File upload** fieldset click to **Choose Files** and find generated `postgresql_schema.sql` -3. Push **Execute** button + *Note: `ENUM` data types are implemented by creating separate data types first using command `CREATE TYPE` (one data type for each `ENUM` column). Then created data type is used as data type for table column.* -### Prepared SQL queries +2. `postgresql_schema_oauth2.sql` that contains table creation commands for Oauth2-related tables. + +3. [Model folder](./Model) contains files with sample SQL queries for each table. Copy-paste them, edit and use. -[Model folder](./Model) contains SQL queries(`SELECT *`, `SELECT`, `INSERT`, `UPDATE`, `DELETE`) usually suggested by `PHPMyAdmin` when you hit `SQL` tab. They are absolutely useless without adaptation to your needs. Copy-paste them then edit. - -Important! Some of SQLs(`INSERT`/`UPDATE`) contains question marks(`?`) which are parameter placeholders. You need to bind values to these params to execute query. - -If your PostgreSQL driver doesn't support named parameters(`PHP PDO` supports while `PHP postgresqli` doesn't) you need to make sure that `namedParametersEnabled` generator option is disabled. +*Note: Important! Some of SQLs(`INSERT`/`UPDATE`) contains question marks(`?`) which are parameter placeholders. You need to bind values to these params to execute query.* diff --git a/samples/schema/petstore/postgresql/postgresql_schema.sql b/samples/schema/petstore/postgresql/postgresql_schema.sql index b4506ad6ce15..bdc196d1e5e8 100644 --- a/samples/schema/petstore/postgresql/postgresql_schema.sql +++ b/samples/schema/petstore/postgresql/postgresql_schema.sql @@ -1,190 +1,118 @@ -/* SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; */ -/* SET AUTOCOMMIT = 0; */ -/* START TRANSACTION; */ -/* SET time_zone = "+00:00"; */ - --- -------------------------------------------------------- - --- --- Table structure for table `ApiResponse` generated from model 'ApiResponse' --- Describes the result of uploading an image resource -- - -CREATE TABLE IF NOT EXISTS `ApiResponse` ( - `code` INT DEFAULT NULL, - `type` TEXT DEFAULT NULL, - `message` TEXT DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Describes the result of uploading an image resource'; - +-- Schema objects for PostgreSQL +-- "OpenAPI Petstore" +-- Created using 'openapi-generator' ('postgresql-schema' generator) +-- (https://openapi-generator.tech/docs/generators/postgresql-schema) -- --- Table structure for table `Category` generated from model 'Category' --- A category for a pet +-- Created: 04.12.2024 22:27:11 -- -CREATE TABLE IF NOT EXISTS `Category` ( - `id` BIGINT DEFAULT NULL, - `name` TEXT DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='A category for a pet'; - -- --- Table structure for table `Order` generated from model 'Order' --- An order for a pets from the pet store --- - -CREATE TABLE IF NOT EXISTS `Order` ( - `id` BIGINT DEFAULT NULL, - `petId` BIGINT DEFAULT NULL, - `quantity` INT DEFAULT NULL, - `shipDate` DATETIME DEFAULT NULL, - `status` ENUM('placed', 'approved', 'delivered') DEFAULT NULL COMMENT 'Order Status', - `complete` TINYINT(1) DEFAULT false -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='An order for a pets from the pet store'; - +-- DROP OBJECTS +-- (remove comment prefix to start using DROP commands) -- --- Table structure for table `Pet` generated from model 'Pet' --- A pet for sale in the pet store +-- TABLES -- - -CREATE TABLE IF NOT EXISTS `Pet` ( - `id` BIGINT DEFAULT NULL, - `category` TEXT DEFAULT NULL, - `name` TEXT NOT NULL, - `photoUrls` JSON NOT NULL, - `tags` JSON DEFAULT NULL, - `status` ENUM('available', 'pending', 'sold') DEFAULT NULL COMMENT 'pet status in the store' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='A pet for sale in the pet store'; +-- DROP TABLE IF EXISTS ApiResponse; +-- DROP TABLE IF EXISTS Category; +-- DROP TABLE IF EXISTS "Order"; +-- DROP TABLE IF EXISTS Pet; +-- DROP TABLE IF EXISTS Tag; +-- DROP TABLE IF EXISTS "User"; -- --- Table structure for table `Tag` generated from model 'Tag' --- A tag for a pet +-- TYPES -- +-- DROP TYPE IF EXISTS Order_status; +-- DROP TYPE IF EXISTS Pet_status; -CREATE TABLE IF NOT EXISTS `Tag` ( - `id` BIGINT DEFAULT NULL, - `name` TEXT DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='A tag for a pet'; -- --- Table structure for table `User` generated from model 'User' --- A User who is purchasing from the pet store +-- CREATE OBJECTS -- - -CREATE TABLE IF NOT EXISTS `User` ( - `id` BIGINT DEFAULT NULL, - `username` TEXT DEFAULT NULL, - `firstName` TEXT DEFAULT NULL, - `lastName` TEXT DEFAULT NULL, - `email` TEXT DEFAULT NULL, - `password` TEXT DEFAULT NULL, - `phone` TEXT DEFAULT NULL, - `userStatus` INT DEFAULT NULL COMMENT 'User Status' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='A User who is purchasing from the pet store'; - - --- --- OAuth2 framework tables --- Thanks to https://github.com/dsquier/oauth2-server-php-postgresql repo +-- TYPES -- +CREATE TYPE Order_status AS ENUM('placed', 'approved', 'delivered'); +CREATE TYPE Pet_status AS ENUM('available', 'pending', 'sold'); -- --- Table structure for table `oauth_clients` --- -CREATE TABLE IF NOT EXISTS `oauth_clients` ( - `client_id` VARCHAR(80) NOT NULL, - `client_secret` VARCHAR(80) DEFAULT NULL, - `redirect_uri` VARCHAR(2000) DEFAULT NULL, - `grant_types` VARCHAR(80) DEFAULT NULL, - `scope` VARCHAR(4000) DEFAULT NULL, - `user_id` VARCHAR(80) DEFAULT NULL, - PRIMARY KEY (`client_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - +-- TABLES -- --- Table structure for table `oauth_access_tokens` -- -CREATE TABLE IF NOT EXISTS `oauth_access_tokens` ( - `access_token` VARCHAR(40) NOT NULL, - `client_id` VARCHAR(80) DEFAULT NULL, - `user_id` VARCHAR(80) DEFAULT NULL, - `expires` TIMESTAMP NOT NULL, - `scope` VARCHAR(4000) DEFAULT NULL, - PRIMARY KEY (`access_token`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - +-- Table 'ApiResponse' generated from model 'ApiResponse' +-- Describes the result of uploading an image resource -- --- Table structure for table `oauth_authorization_codes` --- -CREATE TABLE IF NOT EXISTS `oauth_authorization_codes` ( - `authorization_code` VARCHAR(40) NOT NULL, - `client_id` VARCHAR(80) DEFAULT NULL, - `user_id` VARCHAR(80) DEFAULT NULL, - `redirect_uri` VARCHAR(2000) NOT NULL, - `expires` TIMESTAMP NOT NULL, - `scope` VARCHAR(4000) DEFAULT NULL, - `id_token` VARCHAR(1000) DEFAULT NULL, - PRIMARY KEY (`authorization_code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +CREATE TABLE IF NOT EXISTS ApiResponse ( + code INT DEFAULT NULL, + "type" VARCHAR(255) DEFAULT NULL, + message VARCHAR(255) DEFAULT NULL +); +COMMENT ON TABLE ApiResponse IS 'Describes the result of uploading an image resource'; -- --- Table structure for table `oauth_refresh_tokens` --- -CREATE TABLE IF NOT EXISTS `oauth_refresh_tokens` ( - `refresh_token` VARCHAR(40) NOT NULL, - `client_id` VARCHAR(80) DEFAULT NULL, - `user_id` VARCHAR(80) DEFAULT NULL, - `expires` TIMESTAMP on update CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `scope` VARCHAR(4000) DEFAULT NULL, - PRIMARY KEY (`refresh_token`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - +-- Table 'Category' generated from model 'Category' +-- A category for a pet -- --- Table structure for table `oauth_users` --- -CREATE TABLE IF NOT EXISTS `oauth_users` ( - `username` VARCHAR(80) DEFAULT NULL, - `password` VARCHAR(255) DEFAULT NULL, - `first_name` VARCHAR(80) DEFAULT NULL, - `last_name` VARCHAR(80) DEFAULT NULL, - `email` VARCHAR(2000) DEFAULT NULL, - `email_verified` TINYINT(1) DEFAULT NULL, - `scope` VARCHAR(4000) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +CREATE TABLE IF NOT EXISTS Category ( + id BIGINT DEFAULT NULL, + "name" VARCHAR(255) DEFAULT NULL +); +COMMENT ON TABLE Category IS 'A category for a pet'; -- --- Table structure for table `oauth_scopes` +-- Table 'Order' generated from model 'Order' +-- An order for a pets from the pet store -- -CREATE TABLE IF NOT EXISTS `oauth_scopes` ( - `scope` VARCHAR(80) NOT NULL, - `is_default` TINYINT(1) DEFAULT NULL, - PRIMARY KEY (`scope`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +CREATE TABLE IF NOT EXISTS "Order" ( + id BIGINT DEFAULT NULL, + petId BIGINT DEFAULT NULL, + quantity INT DEFAULT NULL, + shipDate TIMESTAMP DEFAULT NULL, + status Order_status DEFAULT NULL, + complete BOOLEAN DEFAULT 'false' +); +COMMENT ON TABLE "Order" IS 'An order for a pets from the pet store'; +COMMENT ON COLUMN "Order".status IS 'Order Status'; -- --- Table structure for table `oauth_jwt` +-- Table 'Pet' generated from model 'Pet' +-- A pet for sale in the pet store -- -CREATE TABLE IF NOT EXISTS `oauth_jwt` ( - `client_id` VARCHAR(80) NOT NULL, - `subject` VARCHAR(80) DEFAULT NULL, - `public_key` VARCHAR(2000) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +CREATE TABLE IF NOT EXISTS Pet ( + id BIGINT DEFAULT NULL, + category TEXT DEFAULT NULL, + "name" VARCHAR(255) NOT NULL, + photoUrls JSON NOT NULL, + tags JSON DEFAULT NULL, + status Pet_status DEFAULT NULL +); +COMMENT ON TABLE Pet IS 'A pet for sale in the pet store'; +COMMENT ON COLUMN Pet.status IS 'pet status in the store'; -- --- Table structure for table `oauth_jti` +-- Table 'Tag' generated from model 'Tag' +-- A tag for a pet -- -CREATE TABLE IF NOT EXISTS `oauth_jti` ( - `issuer` VARCHAR(80) NOT NULL, - `subject` VARCHAR(80) DEFAULT NULL, - `audience` VARCHAR(80) DEFAULT NULL, - `expires` TIMESTAMP NOT NULL, - `jti` VARCHAR(2000) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +CREATE TABLE IF NOT EXISTS Tag ( + id BIGINT DEFAULT NULL, + "name" VARCHAR(255) DEFAULT NULL +); +COMMENT ON TABLE Tag IS 'A tag for a pet'; -- --- Table structure for table `oauth_public_keys` +-- Table 'User' generated from model 'User' +-- A User who is purchasing from the pet store -- -CREATE TABLE IF NOT EXISTS `oauth_public_keys` ( - `client_id` VARCHAR(80) DEFAULT NULL, - `public_key` VARCHAR(2000) DEFAULT NULL, - `private_key` VARCHAR(2000) DEFAULT NULL, - `encryption_algorithm` VARCHAR(100) DEFAULT 'RS256' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +CREATE TABLE IF NOT EXISTS "User" ( + id BIGINT DEFAULT NULL, + username VARCHAR(255) DEFAULT NULL, + firstName VARCHAR(255) DEFAULT NULL, + lastName VARCHAR(255) DEFAULT NULL, + email VARCHAR(255) DEFAULT NULL, + "password" VARCHAR(255) DEFAULT NULL, + phone VARCHAR(255) DEFAULT NULL, + userStatus INT DEFAULT NULL +); +COMMENT ON TABLE "User" IS 'A User who is purchasing from the pet store'; +COMMENT ON COLUMN "User".userStatus IS 'User Status'; + diff --git a/samples/schema/petstore/postgresql/postgresql_schema_oauth2.sql b/samples/schema/petstore/postgresql/postgresql_schema_oauth2.sql new file mode 100644 index 000000000000..87185038afa4 --- /dev/null +++ b/samples/schema/petstore/postgresql/postgresql_schema_oauth2.sql @@ -0,0 +1,121 @@ +-- +-- OAuth2 framework tables for PostgreSQL +-- Created using 'openapi-generator' ('postgresql-schema' generator) +-- (https://openapi-generator.tech/docs/generators/postgresql-schema) +-- +-- Created: 04.12.2024 22:27:11 +-- + +-- +-- DROP TABLES +-- (remove comment prefix to start using DROP commands) +-- +-- DROP TABLE IF EXISTS oauth_clients; +-- DROP TABLE IF EXISTS oauth_access_tokens; +-- DROP TABLE IF EXISTS oauth_authorization_codes; +-- DROP TABLE IF EXISTS oauth_refresh_tokens; +-- DROP TABLE IF EXISTS oauth_users; +-- DROP TABLE IF EXISTS oauth_scopes; +-- DROP TABLE IF EXISTS oauth_jwt; +-- DROP TABLE IF EXISTS oauth_jti; +-- DROP TABLE IF EXISTS oauth_public_keys; + + +-- +-- Table oauth_clients +-- +CREATE TABLE IF NOT EXISTS oauth_clients ( + client_id VARCHAR(80) NOT NULL PRIMARY KEY, + client_secret VARCHAR(80) DEFAULT NULL, + redirect_uri VARCHAR(2000) DEFAULT NULL, + grant_types VARCHAR(80) DEFAULT NULL, + scope VARCHAR(4000) DEFAULT NULL, + user_id VARCHAR(80) DEFAULT NULL +); + +-- +-- Table oauth_access_tokens +-- +CREATE TABLE IF NOT EXISTS oauth_access_tokens ( + access_token VARCHAR(40) NOT NULL PRIMARY KEY, + client_id VARCHAR(80) DEFAULT NULL, + user_id VARCHAR(80) DEFAULT NULL, + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000) DEFAULT NULL +); + +-- +-- Table oauth_authorization_codes +-- +CREATE TABLE IF NOT EXISTS oauth_authorization_codes ( + authorization_code VARCHAR(40) NOT NULL PRIMARY KEY, + client_id VARCHAR(80) DEFAULT NULL, + user_id VARCHAR(80) DEFAULT NULL, + redirect_uri VARCHAR(2000) NOT NULL, + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000) DEFAULT NULL, + id_token VARCHAR(1000) DEFAULT NULL +); + +-- +-- Table oauth_refresh_tokens +-- +CREATE TABLE IF NOT EXISTS oauth_refresh_tokens ( + refresh_token VARCHAR(40) NOT NULL PRIMARY KEY, + client_id VARCHAR(80), + user_id VARCHAR(80), + expires TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + scope VARCHAR(4000) +); + +-- +-- Table oauth_users +-- +CREATE TABLE IF NOT EXISTS oauth_users ( + username VARCHAR(80) DEFAULT NULL, + password VARCHAR(255) DEFAULT NULL, + first_name VARCHAR(80) DEFAULT NULL, + last_name VARCHAR(80) DEFAULT NULL, + email VARCHAR(2000) DEFAULT NULL, + email_verified SMALLINT DEFAULT NULL, + scope VARCHAR(4000) DEFAULT NULL +); + +-- +-- Table oauth_scopes +-- +CREATE TABLE IF NOT EXISTS oauth_scopes ( + scope VARCHAR(80) NOT NULL PRIMARY KEY, + is_default SMALLINT DEFAULT NULL +); + +-- +-- Table oauth_jwt +-- +CREATE TABLE IF NOT EXISTS oauth_jwt ( + client_id VARCHAR(80) NOT NULL, + subject VARCHAR(80) DEFAULT NULL, + public_key VARCHAR(2000) NOT NULL +); + +-- +-- Table oauth_jti +-- +CREATE TABLE IF NOT EXISTS oauth_jti ( + issuer VARCHAR(80) NOT NULL, + subject VARCHAR(80) DEFAULT NULL, + audience VARCHAR(80) DEFAULT NULL, + expires TIMESTAMP NOT NULL, + jti VARCHAR(2000) NOT NULL +); + +-- +-- Table oauth_public_keys +-- +CREATE TABLE IF NOT EXISTS oauth_public_keys ( + client_id VARCHAR(80) DEFAULT NULL, + public_key VARCHAR(2000) DEFAULT NULL, + private_key VARCHAR(2000) DEFAULT NULL, + encryption_algorithm VARCHAR(100) DEFAULT 'RS256' +); + From e597c709285d9f4fc50ab45a8c9b4b98b9daa9bf Mon Sep 17 00:00:00 2001 From: Ingars Ribners Date: Thu, 5 Dec 2024 01:40:53 +0300 Subject: [PATCH 5/9] Removed current date/time from mustache templates for postgresql-schema generator. Re-created samples. --- .../resources/postgresql-schema/postgresql_schema.mustache | 4 ---- .../postgresql-schema/postgresql_schema_oauth2.mustache | 4 ---- .../main/resources/postgresql-schema/query_examples.mustache | 4 ---- samples/schema/petstore/postgresql/Model/ApiResponse.sql | 2 -- samples/schema/petstore/postgresql/Model/Category.sql | 2 -- samples/schema/petstore/postgresql/Model/Order.sql | 2 -- samples/schema/petstore/postgresql/Model/Pet.sql | 2 -- samples/schema/petstore/postgresql/Model/Tag.sql | 2 -- samples/schema/petstore/postgresql/Model/User.sql | 2 -- samples/schema/petstore/postgresql/postgresql_schema.sql | 2 -- .../schema/petstore/postgresql/postgresql_schema_oauth2.sql | 2 -- 11 files changed, 28 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache index 26dcf1c4e472..1ed02a279f8a 100644 --- a/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache +++ b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema.mustache @@ -3,10 +3,6 @@ -- "{{appName}}" -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) -{{#currDateTime}} --- --- Created: {{{.}}} -{{/currDateTime}} -- -- diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema_oauth2.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema_oauth2.mustache index 6f3fbbf58bfd..07dfdc1d441e 100644 --- a/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema_oauth2.mustache +++ b/modules/openapi-generator/src/main/resources/postgresql-schema/postgresql_schema_oauth2.mustache @@ -3,10 +3,6 @@ -- OAuth2 framework tables for PostgreSQL -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) -{{#currDateTime}} --- --- Created: {{{.}}} -{{/currDateTime}} -- -- diff --git a/modules/openapi-generator/src/main/resources/postgresql-schema/query_examples.mustache b/modules/openapi-generator/src/main/resources/postgresql-schema/query_examples.mustache index 3101b7d6d595..ee9c95e35474 100644 --- a/modules/openapi-generator/src/main/resources/postgresql-schema/query_examples.mustache +++ b/modules/openapi-generator/src/main/resources/postgresql-schema/query_examples.mustache @@ -3,10 +3,6 @@ -- Prepared SQL queries for {{#models}}{{#model}}'{{{name}}}'{{/model}}{{/models}} definition. -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) -{{#currDateTime}} --- --- Created: {{{.}}} -{{/currDateTime}} -- {{#models}}{{#model}} diff --git a/samples/schema/petstore/postgresql/Model/ApiResponse.sql b/samples/schema/petstore/postgresql/Model/ApiResponse.sql index 5ec144897f3a..67cb30e9f49e 100644 --- a/samples/schema/petstore/postgresql/Model/ApiResponse.sql +++ b/samples/schema/petstore/postgresql/Model/ApiResponse.sql @@ -4,8 +4,6 @@ -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) -- --- Created: 04.12.2024 22:27:11 --- -- diff --git a/samples/schema/petstore/postgresql/Model/Category.sql b/samples/schema/petstore/postgresql/Model/Category.sql index 03d8eaacc76e..2b8187dffb4a 100644 --- a/samples/schema/petstore/postgresql/Model/Category.sql +++ b/samples/schema/petstore/postgresql/Model/Category.sql @@ -4,8 +4,6 @@ -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) -- --- Created: 04.12.2024 22:27:11 --- -- diff --git a/samples/schema/petstore/postgresql/Model/Order.sql b/samples/schema/petstore/postgresql/Model/Order.sql index b8f1003f3bc8..78561f89e494 100644 --- a/samples/schema/petstore/postgresql/Model/Order.sql +++ b/samples/schema/petstore/postgresql/Model/Order.sql @@ -4,8 +4,6 @@ -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) -- --- Created: 04.12.2024 22:27:11 --- -- diff --git a/samples/schema/petstore/postgresql/Model/Pet.sql b/samples/schema/petstore/postgresql/Model/Pet.sql index f365dd9ca2b2..389b4eaf12ad 100644 --- a/samples/schema/petstore/postgresql/Model/Pet.sql +++ b/samples/schema/petstore/postgresql/Model/Pet.sql @@ -4,8 +4,6 @@ -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) -- --- Created: 04.12.2024 22:27:11 --- -- diff --git a/samples/schema/petstore/postgresql/Model/Tag.sql b/samples/schema/petstore/postgresql/Model/Tag.sql index 324d1851a098..2622e9446279 100644 --- a/samples/schema/petstore/postgresql/Model/Tag.sql +++ b/samples/schema/petstore/postgresql/Model/Tag.sql @@ -4,8 +4,6 @@ -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) -- --- Created: 04.12.2024 22:27:11 --- -- diff --git a/samples/schema/petstore/postgresql/Model/User.sql b/samples/schema/petstore/postgresql/Model/User.sql index 6035b3be0d8b..cbe46e7f7552 100644 --- a/samples/schema/petstore/postgresql/Model/User.sql +++ b/samples/schema/petstore/postgresql/Model/User.sql @@ -4,8 +4,6 @@ -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) -- --- Created: 04.12.2024 22:27:11 --- -- diff --git a/samples/schema/petstore/postgresql/postgresql_schema.sql b/samples/schema/petstore/postgresql/postgresql_schema.sql index bdc196d1e5e8..0e2e335f4cda 100644 --- a/samples/schema/petstore/postgresql/postgresql_schema.sql +++ b/samples/schema/petstore/postgresql/postgresql_schema.sql @@ -4,8 +4,6 @@ -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) -- --- Created: 04.12.2024 22:27:11 --- -- -- DROP OBJECTS diff --git a/samples/schema/petstore/postgresql/postgresql_schema_oauth2.sql b/samples/schema/petstore/postgresql/postgresql_schema_oauth2.sql index 87185038afa4..aa1be71280b5 100644 --- a/samples/schema/petstore/postgresql/postgresql_schema_oauth2.sql +++ b/samples/schema/petstore/postgresql/postgresql_schema_oauth2.sql @@ -3,8 +3,6 @@ -- Created using 'openapi-generator' ('postgresql-schema' generator) -- (https://openapi-generator.tech/docs/generators/postgresql-schema) -- --- Created: 04.12.2024 22:27:11 --- -- -- DROP TABLES From ddca529ac585824e6d262a88a256f091159a7256 Mon Sep 17 00:00:00 2001 From: Ingars Ribners Date: Thu, 5 Dec 2024 01:49:54 +0300 Subject: [PATCH 6/9] Re-created docs file for postgresql-schema generator. --- docs/generators/postgresql-schema.md | 553 ++++++++++++++++++++++----- 1 file changed, 454 insertions(+), 99 deletions(-) diff --git a/docs/generators/postgresql-schema.md b/docs/generators/postgresql-schema.md index a45a9e32da18..91e9ea4b5a2f 100644 --- a/docs/generators/postgresql-schema.md +++ b/docs/generators/postgresql-schema.md @@ -9,7 +9,7 @@ title: Documentation for the postgresql-schema Generator | generator name | postgresql-schema | pass this to the generate command after -g | | generator stability | BETA | | | generator type | SCHEMA | | -| generator language | PostgreSQL | | +| generator language | Postgresql | | | generator default templating engine | mustache | | | helpTxt | Generates a PostgreSQL schema based on the schema defined in the OpenAPI specification (v2, v3) | | @@ -19,10 +19,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl | Option | Description | Values | Default | | ------ | ----------- | ------ | ------- | |defaultDatabaseName|Default database name for all PostgreSQL queries| || +|idAutoIncEnabled|Generates PostgreSQL sequences for autoincrement feature for integer 'id' fields.| |false| |identifierNamingConvention|Naming convention of PostgreSQL identifiers(table names and column names). This is not related to database name which is defined by defaultDatabaseName option|
    **original**
    Do not transform original names
    **snake_case**
    Use snake_case names
    |original| -|jsonDataType|Use special JSON data types for complex model properties

    *Note: JSON data type requires PostgreSQL version 9.4 or newer*|
    **json**
    Stores data as a text string.
    - Suitable if you need to preserve the data in its original form in human-readable format.
    - Slower in performing operations because the data has to be parsed each time.
    **jsonb**
    - Stores data in a binary format.
    - Supports indexing, which makes search and data retrieval operations faster.
    - Does not preserve the order of keys and may change formatting (e.g., removing extra spaces)
    **off**
    JSON data types disabled. TEXT type will be used to store JSON data
    |json| +|jsonDataType|Use special JSON PostgreSQL data type for complex model properties.| |json| |namedParametersEnabled|Generates model prepared SQLs with named parameters, eg. :petName. Question mark placeholder used when option disabled.| |false| -|idAutoIncEnabled|Add an auto-increment feature for 'id' fields of integer type.|
    **true**
    This generates SERIAL/BIGSERIAL type instead of INT/BIGINT, which automatically handle 'id' field value increments when inserting data into table.
    The choice between SERIAL and BIGSERIAL is based on the format of the OpenAPI schema object 'id' property (int32 to SERIAL and int64 to BIGSERIAL respectively)
    **false**
    - Leave integer 'id' field type as is (INT/BIGINT)
    |false| ## IMPORT MAPPING @@ -41,15 +41,15 @@ These options may be applied as additional-properties (cli) or configOptions (pl
    • BigDecimal
    • ByteArray
    • +
    • Date
    • +
    • DateTime
    • URI
    • UUID
    • -
    • binary
    • bool
    • boolean
    • byte
    • char
    • date
    • -
    • DateTime
    • double
    • file
    • float
    • @@ -65,102 +65,457 @@ These options may be applied as additional-properties (cli) or configOptions (pl ## RESERVED WORDS -| ABORT | ABSOLUTE | ACCESS | ACTION | ADD | -|------------|-----------|------------|-----------|-----------| -| ADMIN | AFTER | AGGREGATE | ALL | ALSO | -| ALTER | ALWAYS | ANALYSE | ANALYZE | AND | -| ANY | ARRAY | AS | ASC | ASSERTION | -| ASSIGNMENT | ASYMMETRIC| AT | ATTACH | ATTRIBUTE | -| AUTHORIZATION | BACKWARD | BEFORE | BEGIN | BETWEEN | -| BIGINT | BINARY | BIT | BOOLEAN | BOTH | -| BY | CACHE | CALL | CALLED | CASCADE | -| CASCADED | CASE | CAST | CATALOG | CHAIN | -| CHAR | CHARACTER | CHARACTERISTICS | CHECK | CHECKPOINT| -| CLASS | CLOSE | CLUSTER | COALESCE | COLLATE | -| COLLATION | COLUMN | COLUMNS | COMMENT | COMMENTS | -| COMMIT | COMMITTED | CONCURRENTLY | CONFIGURATION | CONFLICT | -| CONNECTION | CONSTRAINT| CONSTRAINTS| CONTENT | CONTINUE | -| CONVERSION | COPY | COST | CREATE | CROSS | -| CSV | CUBE | CURRENT | CURRENT_CATALOG | CURRENT_DATE | -| CURRENT_ROLE | CURRENT_SCHEMA | CURRENT_TIME | CURRENT_TIMESTAMP | CURRENT_USER | -| CURSOR | CYCLE | DATA | DATABASE | DAY | -| DEALLOCATE | DEC | DECIMAL | DECLARE | DEFAULT | -| DEFAULTS | DEFERRABLE| DEFERRED | DEFINE | DEFINER | -| DELETE | DELIMITER | DELIMITERS | DEPENDS | DEPTH | -| DESC | DESCRIBE | DETACH | DICTIONARY| DISABLE | -| DISCARD | DISTINCT | DO | DOCUMENT | DOMAIN | -| DOUBLE | DROP | EACH | ELSE | ENABLE | -| ENCODING | ENCRYPTED | END | ENUM | ESCAPE | -| EVENT | EXCEPT | EXCLUDE | EXCLUDING | EXCLUSIVE | -| EXECUTE | EXISTS | EXPLAIN | EXTENSION | EXTERNAL | -| EXTRACT | FALSE | FAMILY | FETCH | FILTER | -| FINALIZE | FIRST | FLOAT | FOLLOWING | FOR | -| FORCE | FOREIGN | FORWARD | FREEZE | FROM | -| FULL | FUNCTION | FUNCTIONS | GENERATED | GLOBAL | -| GRANT | GRANTED | GREATEST | GROUP | GROUPING | -| HANDLER | HAVING | HEADER | HOLD | HOUR | -| IDENTITY | IF | ILIKE | IMMEDIATE | IMMUTABLE | -| IMPLICIT | IMPORT | IN | INCLUDING | INCREMENT | -| INDEX | INDEXES | INHERIT | INHERITS | INITIALLY | -| INLINE | INNER | INOUT | INPUT | INSENSITIVE | -| INSERT | INSTEAD | INT | INTEGER | INTERSECT | -| INTERVAL | INTO | INVOKER | IS | ISNULL | -| ISOLATION | JOIN | KEY | LABEL | LANGUAGE | -| LARGE | LAST | LATERAL | LEADING | LEAKPROOF | -| LEAST | LEFT | LEVEL | LIKE | LIMIT | -| LISTEN | LOAD | LOCAL | LOCALTIME | LOCALTIMESTAMP | -| LOCATION | LOCK | LOCKED | LOGGED | MAPPING | -| MATCH | MATERIALIZED | MAXVALUE | METHOD | MINUTE | -| MINVALUE | MODE | MODIFY | MONTH | MOVE | -| NAME | NAMES | NATIONAL | NATURAL | NCHAR | -| NEW | NEXT | NO | NONE | NORMALIZE | -| NORMALIZED | NOT | NOTHING | NOTIFY | NOTNULL | -| NOWAIT | NULL | NULLABLE | NULLIF | NUMERIC | -| OBJECT | OF | OFF | OFFSET | OIDS | -| OLD | ON | ONLY | OPERATOR | OPTION | -| OPTIONS | OR | ORDER | ORDINALITY | OTHERS | -| OUT | OUTER | OVER | OVERLAPS | OVERLAY | -| OVERRIDING | OWNED | OWNER | PARALLEL | PARAMETER | -| PARSER | PARTIAL | PARTITION | PASSING | PASSWORD | -| PLACING | PLANS | POLICY | POSITION | PRECEDING | -| PRECISION | PREPARE | PREPARED | PRESERVE | PRIMARY | -| PRIOR | PRIVILEGES | PROCEDURAL | PROCEDURE | PROGRAM | -| PUBLICATION| QUOTE | RANGE | READ | REAL | -| REASSIGN | RECHECK | RECURSIVE | REF | REFERENCES | -| REFERENCING| REFRESH | REINDEX | RELATIVE | RELEASE | -| RENAME | REPEATABLE | REPLACE | REPLICA | RESET | -| RESTART | RESTRICT | RETURNING | RETURNS | REVOKE | -| RIGHT | ROLE | ROLLBACK | ROLLUP | ROUTINE | -| ROUTINES | ROW | ROWS | RULE | SAVEPOINT | -| SCHEMA | SCHEMAS | SCROLL | SEARCH | SECOND | -| SECURITY | SELECT | SEQUENCE | SEQUENCES | SERIALIZABLE| -| SERVER | SESSION | SESSION_USER| SET | SETOF | -| SETS | SHARE | SHOW | SIMILAR | SIMPLE | -| SKIP | SMALLINT | SNAPSHOT | SOME | SQL | -| STABLE | STANDALONE | START | STATEMENT | STATISTICS | -| STDIN | STDOUT | STORAGE | STRICT | STRIP | -| SUBSCRIPTION| SUBSTRING | SYMMETRIC | SYSID | SYSTEM | -| TABLE | TABLES | TABLESAMPLE| TABLESPACE | TEMP | -| TEMPLATE | TEMPORARY | TEXT | THEN | TIES | -| TIME | TIMESTAMP | TO | TRAILING | TRANSACTION| -| TRANSFORM | TREAT | TRIGGER | TRIM | TRUE | -| TRUNCATE | TRUSTED | TYPE | TYPES | UNBOUNDED | -| UNCOMMITTED| UNENCRYPTED| UNION | UNIQUE | UNKNOWN | -| UNLISTEN | UNLOGGED | UNTIL | UPDATE | USER | -| USING | VACUUM | VALID | VALIDATE | VALIDATOR | -| VALUE | VALUES | VARCHAR | VARIADIC | VARYING | -| VERBOSE | VERSION | VIEW | VIEWS | VOLATILE | -| WHEN | WHERE | WHITESPACE | WINDOW | WITH | -| WITHIN | WITHOUT | WORK | WRAPPER | WRITE | -| XML | XMLATTRIBUTES| XMLCONCAT | XMLELEMENT | XMLEXISTS | -| XMLFOREST | XMLNAMESPACES| XMLPARSE | XMLPI | XMLROOT | -| XMLSERIALIZE| XMLTABLE | YEAR | YES | ZONE | - -Source: [Full list of keywords in the PostgreSQL 17](https://www.postgresql.org/docs/17/sql-keywords-appendix.html) - +
        +
      • abort
      • +
      • absolute
      • +
      • access
      • +
      • action
      • +
      • add
      • +
      • admin
      • +
      • after
      • +
      • aggregate
      • +
      • all
      • +
      • also
      • +
      • alter
      • +
      • always
      • +
      • analyse
      • +
      • analyze
      • +
      • and
      • +
      • any
      • +
      • array
      • +
      • as
      • +
      • asc
      • +
      • assertion
      • +
      • assignment
      • +
      • asymmetric
      • +
      • at
      • +
      • attach
      • +
      • attribute
      • +
      • authorization
      • +
      • backward
      • +
      • before
      • +
      • begin
      • +
      • between
      • +
      • bigint
      • +
      • binary
      • +
      • bit
      • +
      • boolean
      • +
      • both
      • +
      • by
      • +
      • cache
      • +
      • call
      • +
      • called
      • +
      • cascade
      • +
      • cascaded
      • +
      • case
      • +
      • cast
      • +
      • catalog
      • +
      • chain
      • +
      • char
      • +
      • character
      • +
      • characteristics
      • +
      • check
      • +
      • checkpoint
      • +
      • class
      • +
      • close
      • +
      • cluster
      • +
      • coalesce
      • +
      • collate
      • +
      • collation
      • +
      • column
      • +
      • columns
      • +
      • comment
      • +
      • comments
      • +
      • commit
      • +
      • committed
      • +
      • concurrently
      • +
      • configuration
      • +
      • conflict
      • +
      • connection
      • +
      • constraint
      • +
      • constraints
      • +
      • content
      • +
      • continue
      • +
      • conversion
      • +
      • copy
      • +
      • cost
      • +
      • create
      • +
      • cross
      • +
      • csv
      • +
      • cube
      • +
      • current
      • +
      • current_catalog
      • +
      • current_date
      • +
      • current_role
      • +
      • current_schema
      • +
      • current_time
      • +
      • current_timestamp
      • +
      • current_user
      • +
      • cursor
      • +
      • cycle
      • +
      • data
      • +
      • database
      • +
      • day
      • +
      • deallocate
      • +
      • dec
      • +
      • decimal
      • +
      • declare
      • +
      • default
      • +
      • defaults
      • +
      • deferrable
      • +
      • deferred
      • +
      • define
      • +
      • definer
      • +
      • delete
      • +
      • delimiter
      • +
      • delimiters
      • +
      • depends
      • +
      • depth
      • +
      • desc
      • +
      • describe
      • +
      • detach
      • +
      • dictionary
      • +
      • disable
      • +
      • discard
      • +
      • distinct
      • +
      • do
      • +
      • document
      • +
      • domain
      • +
      • double
      • +
      • drop
      • +
      • each
      • +
      • else
      • +
      • enable
      • +
      • encoding
      • +
      • encrypted
      • +
      • end
      • +
      • enum
      • +
      • escape
      • +
      • event
      • +
      • except
      • +
      • exclude
      • +
      • excluding
      • +
      • exclusive
      • +
      • execute
      • +
      • exists
      • +
      • explain
      • +
      • extension
      • +
      • external
      • +
      • extract
      • +
      • false
      • +
      • family
      • +
      • fetch
      • +
      • filter
      • +
      • finalize
      • +
      • first
      • +
      • float
      • +
      • following
      • +
      • for
      • +
      • force
      • +
      • foreign
      • +
      • forward
      • +
      • freeze
      • +
      • from
      • +
      • full
      • +
      • function
      • +
      • functions
      • +
      • generated
      • +
      • global
      • +
      • grant
      • +
      • granted
      • +
      • greatest
      • +
      • group
      • +
      • grouping
      • +
      • handler
      • +
      • having
      • +
      • header
      • +
      • hold
      • +
      • hour
      • +
      • identity
      • +
      • if
      • +
      • ilike
      • +
      • immediate
      • +
      • immutable
      • +
      • implicit
      • +
      • import
      • +
      • in
      • +
      • including
      • +
      • increment
      • +
      • index
      • +
      • indexes
      • +
      • inherit
      • +
      • inherits
      • +
      • initially
      • +
      • inline
      • +
      • inner
      • +
      • inout
      • +
      • input
      • +
      • insensitive
      • +
      • insert
      • +
      • instead
      • +
      • int
      • +
      • integer
      • +
      • intersect
      • +
      • interval
      • +
      • into
      • +
      • invoker
      • +
      • is
      • +
      • isnull
      • +
      • isolation
      • +
      • join
      • +
      • key
      • +
      • label
      • +
      • language
      • +
      • large
      • +
      • last
      • +
      • lateral
      • +
      • leading
      • +
      • leakproof
      • +
      • least
      • +
      • left
      • +
      • level
      • +
      • like
      • +
      • limit
      • +
      • listen
      • +
      • load
      • +
      • local
      • +
      • localtime
      • +
      • localtimestamp
      • +
      • location
      • +
      • lock
      • +
      • locked
      • +
      • logged
      • +
      • mapping
      • +
      • match
      • +
      • materialized
      • +
      • maxvalue
      • +
      • method
      • +
      • minute
      • +
      • minvalue
      • +
      • mode
      • +
      • modify
      • +
      • month
      • +
      • move
      • +
      • name
      • +
      • names
      • +
      • national
      • +
      • natural
      • +
      • nchar
      • +
      • new
      • +
      • next
      • +
      • no
      • +
      • none
      • +
      • normalize
      • +
      • normalized
      • +
      • not
      • +
      • nothing
      • +
      • notify
      • +
      • notnull
      • +
      • nowait
      • +
      • null
      • +
      • nullable
      • +
      • nullif
      • +
      • numeric
      • +
      • object
      • +
      • of
      • +
      • off
      • +
      • offset
      • +
      • oids
      • +
      • old
      • +
      • on
      • +
      • only
      • +
      • operator
      • +
      • option
      • +
      • options
      • +
      • or
      • +
      • order
      • +
      • ordinality
      • +
      • others
      • +
      • out
      • +
      • outer
      • +
      • over
      • +
      • overlaps
      • +
      • overlay
      • +
      • overriding
      • +
      • owned
      • +
      • owner
      • +
      • parallel
      • +
      • parameter
      • +
      • parser
      • +
      • partial
      • +
      • partition
      • +
      • passing
      • +
      • password
      • +
      • placing
      • +
      • plans
      • +
      • policy
      • +
      • position
      • +
      • preceding
      • +
      • precision
      • +
      • prepare
      • +
      • prepared
      • +
      • preserve
      • +
      • primary
      • +
      • prior
      • +
      • privileges
      • +
      • procedural
      • +
      • procedure
      • +
      • program
      • +
      • publication
      • +
      • quote
      • +
      • range
      • +
      • read
      • +
      • real
      • +
      • reassign
      • +
      • recheck
      • +
      • recursive
      • +
      • ref
      • +
      • references
      • +
      • referencing
      • +
      • refresh
      • +
      • reindex
      • +
      • relative
      • +
      • release
      • +
      • rename
      • +
      • repeatable
      • +
      • replace
      • +
      • replica
      • +
      • reset
      • +
      • restart
      • +
      • restrict
      • +
      • returning
      • +
      • returns
      • +
      • revoke
      • +
      • right
      • +
      • role
      • +
      • rollback
      • +
      • rollup
      • +
      • routine
      • +
      • routines
      • +
      • row
      • +
      • rows
      • +
      • rule
      • +
      • savepoint
      • +
      • schema
      • +
      • schemas
      • +
      • scroll
      • +
      • search
      • +
      • second
      • +
      • security
      • +
      • select
      • +
      • sequence
      • +
      • sequences
      • +
      • serializable
      • +
      • server
      • +
      • session
      • +
      • session_user
      • +
      • set
      • +
      • setof
      • +
      • sets
      • +
      • share
      • +
      • show
      • +
      • similar
      • +
      • simple
      • +
      • skip
      • +
      • smallint
      • +
      • snapshot
      • +
      • some
      • +
      • sql
      • +
      • stable
      • +
      • standalone
      • +
      • start
      • +
      • statement
      • +
      • statistics
      • +
      • stdin
      • +
      • stdout
      • +
      • storage
      • +
      • strict
      • +
      • strip
      • +
      • subscription
      • +
      • substring
      • +
      • symmetric
      • +
      • sysid
      • +
      • system
      • +
      • table
      • +
      • tables
      • +
      • tablesample
      • +
      • tablespace
      • +
      • temp
      • +
      • template
      • +
      • temporary
      • +
      • text
      • +
      • then
      • +
      • ties
      • +
      • time
      • +
      • timestamp
      • +
      • to
      • +
      • trailing
      • +
      • transaction
      • +
      • transform
      • +
      • treat
      • +
      • trigger
      • +
      • trim
      • +
      • true
      • +
      • truncate
      • +
      • trusted
      • +
      • type
      • +
      • types
      • +
      • unbounded
      • +
      • uncommitted
      • +
      • unencrypted
      • +
      • union
      • +
      • unique
      • +
      • unknown
      • +
      • unlisten
      • +
      • unlogged
      • +
      • until
      • +
      • update
      • +
      • user
      • +
      • using
      • +
      • vacuum
      • +
      • valid
      • +
      • validate
      • +
      • validator
      • +
      • value
      • +
      • values
      • +
      • varchar
      • +
      • variadic
      • +
      • varying
      • +
      • verbose
      • +
      • version
      • +
      • view
      • +
      • views
      • +
      • volatile
      • +
      • when
      • +
      • where
      • +
      • whitespace
      • +
      • window
      • +
      • with
      • +
      • within
      • +
      • without
      • +
      • work
      • +
      • wrapper
      • +
      • write
      • +
      • xml
      • +
      • xmlattributes
      • +
      • xmlconcat
      • +
      • xmlelement
      • +
      • xmlexists
      • +
      • xmlforest
      • +
      • xmlnamespaces
      • +
      • xmlparse
      • +
      • xmlpi
      • +
      • xmlroot
      • +
      • xmlserialize
      • +
      • xmltable
      • +
      • year
      • +
      • yes
      • +
      • zone
      • +
      ## FEATURE SET + ### Client Modification Feature | Name | Supported | Defined By | | ---- | --------- | ---------- | @@ -171,7 +526,7 @@ Source: [Full list of keywords in the PostgreSQL 17](https://www.postgresql.org/ ### Data Type Feature | Name | Supported | Defined By | -| ---- | --------- | ---------- |s +| ---- | --------- | ---------- | |Custom|✗|OAS2,OAS3 |Int32|✓|OAS2,OAS3 |Int64|✓|OAS2,OAS3 From da24e3ad6ff1c7ad238a2f81b2d77c22cbf0f3a8 Mon Sep 17 00:00:00 2001 From: Ingars Ribners Date: Thu, 5 Dec 2024 02:18:01 +0300 Subject: [PATCH 7/9] Removed unecessary LocalTime computing code and imports for postgresql-schema generator. --- .../codegen/languages/PostgresqlSchemaCodegen.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java index b6b7914e6b32..cb666ed1acce 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java @@ -34,9 +34,6 @@ import java.util.regex.Pattern; import java.io.File; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - import static org.openapitools.codegen.utils.StringUtils.underscore; @SuppressWarnings("unchecked") @@ -283,11 +280,6 @@ public String getHelp() { public void processOpts() { super.processOpts(); - LocalDateTime now = LocalDateTime.now(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss"); - String formattedDateTime = now.format(formatter); - additionalProperties.put("currDateTime", formattedDateTime); - if (additionalProperties.containsKey(DEFAULT_DATABASE_NAME)) { if (additionalProperties.get(DEFAULT_DATABASE_NAME).equals("")) { additionalProperties.remove(DEFAULT_DATABASE_NAME); From 3c781aa42127c55b1cd1309e55fce923dc4133c4 Mon Sep 17 00:00:00 2001 From: Ingars Ribners Date: Thu, 5 Dec 2024 22:40:01 +0300 Subject: [PATCH 8/9] Errors fixed for postgresql-schema generator. Samples recreated. --- docs/generators/postgresql-schema.md | 410 +++++++++++++++++- .../languages/PostgresqlSchemaCodegen.java | 347 +++++++++------ .../PostgresqlSchemaCodegenTest.java | 167 ++++--- .../PostgresqlSchemaOptionsTest.java | 1 + .../petstore/postgresql/Model/ApiResponse.sql | 16 +- .../petstore/postgresql/Model/Category.sql | 16 +- .../petstore/postgresql/Model/Order.sql | 16 +- .../schema/petstore/postgresql/Model/Pet.sql | 16 +- .../schema/petstore/postgresql/Model/Tag.sql | 16 +- .../schema/petstore/postgresql/Model/User.sql | 16 +- .../petstore/postgresql/postgresql_schema.sql | 115 ++--- 11 files changed, 809 insertions(+), 327 deletions(-) diff --git a/docs/generators/postgresql-schema.md b/docs/generators/postgresql-schema.md index 91e9ea4b5a2f..b4959234ae6e 100644 --- a/docs/generators/postgresql-schema.md +++ b/docs/generators/postgresql-schema.md @@ -18,11 +18,11 @@ These options may be applied as additional-properties (cli) or configOptions (pl | Option | Description | Values | Default | | ------ | ----------- | ------ | ------- | -|defaultDatabaseName|Default database name for all PostgreSQL queries| || -|idAutoIncEnabled|Generates PostgreSQL sequences for autoincrement feature for integer 'id' fields.| |false| -|identifierNamingConvention|Naming convention of PostgreSQL identifiers(table names and column names). This is not related to database name which is defined by defaultDatabaseName option|
      **original**
      Do not transform original names
      **snake_case**
      Use snake_case names
      |original| -|jsonDataType|Use special JSON PostgreSQL data type for complex model properties.| |json| -|namedParametersEnabled|Generates model prepared SQLs with named parameters, eg. :petName. Question mark placeholder used when option disabled.| |false| +|defaultDatabaseName|Database name that will be used for all generated PostgreSQL DDL and DML statements.| || +|idAutoIncEnabled|If `true`, generates autoincrement PostgreSQL types `SERIAL` and `BIGSERIAL` for `int32` and `int64` respectively for integer fields with name 'id'.| |false| +|identifierNamingConvention|Naming convention of PostgreSQL idebntifiers (table names and column names).|
      **snake_case**
      Transform named to 'snake_case'.
      **original**
      Leave original names as in `YAML` file.
      |snake_case| +|jsonDataType|Use of PostgreSQL data types for complex model properties.|
      **json**
      Generate `JSON` fields. Value is stored in `JSON` data type field as human-readable text. Value compliance with JSON standard is checked.
      **jsonb**
      Generate `JSONB` fields. Value is stored in `JSONB` data type field in binary format. `JSONB` data type is generally nore efficient than `JSON` but it is not human-readable. Value compliance with JSON standard is checked.
      **off**
      Generate `TEXT` fields. Just store the value as plain text. Value compliance with JSON standard is not checked.
      |json| +|namedParametersEnabled|Generates query examples with named variables in value placeholders (eg.`:name`,`:quantity`) if `true`. Otherwise, generates question marks `?` in value placeholders.| |false| ## IMPORT MAPPING @@ -66,15 +66,22 @@ These options may be applied as additional-properties (cli) or configOptions (pl ## RESERVED WORDS
        +
      • a
      • abort
      • +
      • abs
      • +
      • absent
      • absolute
      • access
      • +
      • according
      • +
      • acos
      • action
      • +
      • ada
      • add
      • admin
      • after
      • aggregate
      • all
      • +
      • allocate
      • also
      • alter
      • always
      • @@ -82,82 +89,158 @@ These options may be applied as additional-properties (cli) or configOptions (pl
      • analyze
      • and
      • any
      • +
      • any_value
      • +
      • are
      • array
      • +
      • array_agg
      • +
      • array_max_cardinality
      • as
      • asc
      • +
      • asensitive
      • +
      • asin
      • assertion
      • assignment
      • asymmetric
      • at
      • +
      • atan
      • +
      • atomic
      • attach
      • attribute
      • +
      • attributes
      • authorization
      • +
      • avg
      • backward
      • +
      • base64
      • before
      • begin
      • +
      • begin_frame
      • +
      • begin_partition
      • +
      • bernoulli
      • between
      • bigint
      • binary
      • bit
      • +
      • bit_length
      • +
      • blob
      • +
      • blocked
      • +
      • bom
      • boolean
      • both
      • +
      • breadth
      • +
      • btrim
      • by
      • +
      • c
      • cache
      • call
      • called
      • +
      • cardinality
      • cascade
      • cascaded
      • case
      • cast
      • catalog
      • +
      • catalog_name
      • +
      • ceil
      • +
      • ceiling
      • chain
      • +
      • chaining
      • char
      • +
      • char_length
      • character
      • +
      • character_length
      • +
      • character_set_catalog
      • +
      • character_set_name
      • +
      • character_set_schema
      • characteristics
      • +
      • characters
      • check
      • checkpoint
      • class
      • +
      • class_origin
      • +
      • classifier
      • +
      • clob
      • close
      • cluster
      • coalesce
      • +
      • cobol
      • collate
      • collation
      • +
      • collation_catalog
      • +
      • collation_name
      • +
      • collation_schema
      • +
      • collect
      • column
      • +
      • column_name
      • columns
      • +
      • command_function
      • +
      • command_function_code
      • comment
      • comments
      • commit
      • committed
      • +
      • compression
      • concurrently
      • +
      • condition
      • +
      • condition_number
      • +
      • conditional
      • configuration
      • conflict
      • +
      • connect
      • connection
      • +
      • connection_name
      • constraint
      • +
      • constraint_catalog
      • +
      • constraint_name
      • +
      • constraint_schema
      • constraints
      • +
      • constructor
      • +
      • contains
      • content
      • continue
      • +
      • control
      • conversion
      • +
      • convert
      • +
      • copartition
      • copy
      • +
      • corr
      • +
      • corresponding
      • +
      • cos
      • +
      • cosh
      • cost
      • +
      • count
      • +
      • covar_pop
      • +
      • covar_samp
      • create
      • cross
      • csv
      • cube
      • +
      • cume_dist
      • current
      • current_catalog
      • current_date
      • +
      • current_default_transform_group
      • +
      • current_path
      • current_role
      • +
      • current_row
      • current_schema
      • current_time
      • current_timestamp
      • +
      • current_transform_group_for_type
      • current_user
      • cursor
      • +
      • cursor_name
      • cycle
      • data
      • database
      • +
      • datalink
      • +
      • date
      • +
      • datetime_interval_code
      • +
      • datetime_interval_precision
      • day
      • +
      • db
      • deallocate
      • dec
      • +
      • decfloat
      • decimal
      • declare
      • default
      • @@ -165,86 +248,153 @@ These options may be applied as additional-properties (cli) or configOptions (pl
      • deferrable
      • deferred
      • define
      • +
      • defined
      • definer
      • +
      • degree
      • delete
      • delimiter
      • delimiters
      • +
      • dense_rank
      • depends
      • depth
      • +
      • deref
      • +
      • derived
      • desc
      • describe
      • +
      • descriptor
      • detach
      • +
      • deterministic
      • +
      • diagnostics
      • dictionary
      • disable
      • discard
      • +
      • disconnect
      • +
      • dispatch
      • distinct
      • +
      • dlnewcopy
      • +
      • dlpreviouscopy
      • +
      • dlurlcomplete
      • +
      • dlurlcompleteonly
      • +
      • dlurlcompletewrite
      • +
      • dlurlpath
      • +
      • dlurlpathonly
      • +
      • dlurlpathwrite
      • +
      • dlurlscheme
      • +
      • dlurlserver
      • +
      • dlvalue
      • do
      • document
      • domain
      • double
      • drop
      • +
      • dynamic
      • +
      • dynamic_function
      • +
      • dynamic_function_code
      • each
      • +
      • element
      • else
      • +
      • empty
      • enable
      • encoding
      • encrypted
      • end
      • +
      • end-exec
      • +
      • end_frame
      • +
      • end_partition
      • +
      • enforced
      • enum
      • +
      • equals
      • +
      • error
      • escape
      • event
      • +
      • every
      • except
      • +
      • exception
      • exclude
      • excluding
      • exclusive
      • +
      • exec
      • execute
      • exists
      • +
      • exp
      • explain
      • +
      • expression
      • extension
      • external
      • extract
      • false
      • family
      • fetch
      • +
      • file
      • filter
      • +
      • final
      • finalize
      • +
      • finish
      • first
      • +
      • first_value
      • +
      • flag
      • float
      • +
      • floor
      • following
      • for
      • force
      • foreign
      • +
      • format
      • +
      • fortran
      • forward
      • +
      • found
      • +
      • frame_row
      • +
      • free
      • freeze
      • from
      • +
      • fs
      • +
      • fulfill
      • full
      • function
      • functions
      • +
      • fusion
      • +
      • g
      • +
      • general
      • generated
      • +
      • get
      • global
      • +
      • go
      • +
      • goto
      • grant
      • granted
      • greatest
      • group
      • grouping
      • +
      • groups
      • handler
      • having
      • header
      • +
      • hex
      • +
      • hierarchy
      • hold
      • hour
      • +
      • id
      • identity
      • if
      • +
      • ignore
      • ilike
      • immediate
      • +
      • immediately
      • immutable
      • +
      • implementation
      • implicit
      • import
      • in
      • +
      • include
      • including
      • increment
      • +
      • indent
      • index
      • indexes
      • +
      • indicator
      • inherit
      • inherits
      • +
      • initial
      • initially
      • inline
      • inner
      • @@ -252,10 +402,14 @@ These options may be applied as additional-properties (cli) or configOptions (pl
      • input
      • insensitive
      • insert
      • +
      • instance
      • +
      • instantiable
      • instead
      • int
      • integer
      • +
      • integrity
      • intersect
      • +
      • intersection
      • interval
      • into
      • invoker
      • @@ -263,46 +417,107 @@ These options may be applied as additional-properties (cli) or configOptions (pl
      • isnull
      • isolation
      • join
      • +
      • json
      • +
      • json_array
      • +
      • json_arrayagg
      • +
      • json_exists
      • +
      • json_object
      • +
      • json_objectagg
      • +
      • json_query
      • +
      • json_scalar
      • +
      • json_serialize
      • +
      • json_table
      • +
      • json_table_primitive
      • +
      • json_value
      • +
      • k
      • +
      • keep
      • key
      • +
      • key_member
      • +
      • key_type
      • +
      • keys
      • label
      • +
      • lag
      • language
      • large
      • last
      • +
      • last_value
      • lateral
      • +
      • lead
      • leading
      • leakproof
      • least
      • left
      • +
      • length
      • level
      • +
      • library
      • like
      • +
      • like_regex
      • limit
      • +
      • link
      • +
      • listagg
      • listen
      • +
      • ln
      • load
      • local
      • localtime
      • localtimestamp
      • location
      • +
      • locator
      • lock
      • locked
      • +
      • log
      • +
      • log10
      • logged
      • +
      • lower
      • +
      • lpad
      • +
      • ltrim
      • +
      • m
      • +
      • map
      • mapping
      • match
      • +
      • match_number
      • +
      • match_recognize
      • +
      • matched
      • +
      • matches
      • materialized
      • +
      • max
      • maxvalue
      • +
      • measures
      • +
      • member
      • +
      • merge
      • +
      • merge_action
      • +
      • message_length
      • +
      • message_octet_length
      • +
      • message_text
      • method
      • +
      • min
      • minute
      • minvalue
      • +
      • mod
      • mode
      • -
      • modify
      • +
      • modifies
      • +
      • module
      • month
      • +
      • more
      • move
      • +
      • multiset
      • +
      • mumps
      • name
      • names
      • +
      • namespace
      • national
      • natural
      • nchar
      • +
      • nclob
      • +
      • nested
      • +
      • nesting
      • new
      • next
      • +
      • nfc
      • +
      • nfd
      • +
      • nfkc
      • +
      • nfkd
      • +
      • nil
      • no
      • none
      • normalize
      • @@ -312,67 +527,130 @@ These options may be applied as additional-properties (cli) or configOptions (pl
      • notify
      • notnull
      • nowait
      • +
      • nth_value
      • +
      • ntile
      • null
      • +
      • null_ordering
      • nullable
      • nullif
      • +
      • nulls
      • +
      • number
      • numeric
      • object
      • +
      • occurrence
      • +
      • occurrences_regex
      • +
      • octet_length
      • +
      • octets
      • of
      • off
      • offset
      • oids
      • old
      • +
      • omit
      • on
      • +
      • one
      • only
      • +
      • open
      • operator
      • option
      • options
      • or
      • order
      • +
      • ordering
      • ordinality
      • others
      • out
      • outer
      • +
      • output
      • over
      • +
      • overflow
      • overlaps
      • overlay
      • overriding
      • owned
      • owner
      • +
      • p
      • +
      • pad
      • parallel
      • parameter
      • +
      • parameter_mode
      • +
      • parameter_name
      • +
      • parameter_ordinal_position
      • +
      • parameter_specific_catalog
      • +
      • parameter_specific_name
      • +
      • parameter_specific_schema
      • parser
      • partial
      • partition
      • +
      • pascal
      • +
      • pass
      • passing
      • +
      • passthrough
      • password
      • +
      • past
      • +
      • path
      • +
      • pattern
      • +
      • per
      • +
      • percent
      • +
      • percent_rank
      • +
      • percentile_cont
      • +
      • percentile_disc
      • +
      • period
      • +
      • permission
      • +
      • permute
      • +
      • pipe
      • placing
      • +
      • plan
      • plans
      • +
      • pli
      • policy
      • +
      • portion
      • position
      • +
      • position_regex
      • +
      • power
      • +
      • precedes
      • preceding
      • precision
      • prepare
      • prepared
      • preserve
      • +
      • prev
      • primary
      • prior
      • +
      • private
      • privileges
      • procedural
      • procedure
      • +
      • procedures
      • program
      • +
      • prune
      • +
      • ptf
      • +
      • public
      • publication
      • quote
      • +
      • quotes
      • range
      • +
      • rank
      • read
      • +
      • reads
      • real
      • reassign
      • recheck
      • +
      • recovery
      • recursive
      • ref
      • references
      • referencing
      • refresh
      • +
      • regr_avgx
      • +
      • regr_avgy
      • +
      • regr_count
      • +
      • regr_intercept
      • +
      • regr_r2
      • +
      • regr_slope
      • +
      • regr_sxx
      • +
      • regr_sxy
      • +
      • regr_syy
      • reindex
      • relative
      • release
      • @@ -380,9 +658,18 @@ These options may be applied as additional-properties (cli) or configOptions (pl
      • repeatable
      • replace
      • replica
      • +
      • requiring
      • reset
      • +
      • respect
      • restart
      • +
      • restore
      • restrict
      • +
      • result
      • +
      • return
      • +
      • returned_cardinality
      • +
      • returned_length
      • +
      • returned_octet_length
      • +
      • returned_sqlstate
      • returning
      • returns
      • revoke
      • @@ -391,22 +678,44 @@ These options may be applied as additional-properties (cli) or configOptions (pl
      • rollback
      • rollup
      • routine
      • +
      • routine_catalog
      • +
      • routine_name
      • +
      • routine_schema
      • routines
      • row
      • +
      • row_count
      • +
      • row_number
      • rows
      • +
      • rpad
      • +
      • rtrim
      • rule
      • +
      • running
      • savepoint
      • +
      • scalar
      • +
      • scale
      • schema
      • +
      • schema_name
      • schemas
      • +
      • scope
      • +
      • scope_catalog
      • +
      • scope_name
      • +
      • scope_schema
      • scroll
      • search
      • second
      • +
      • section
      • security
      • +
      • seek
      • select
      • +
      • selective
      • +
      • self
      • +
      • semantics
      • +
      • sensitive
      • sequence
      • sequences
      • serializable
      • server
      • +
      • server_name
      • session
      • session_user
      • set
      • @@ -416,79 +725,157 @@ These options may be applied as additional-properties (cli) or configOptions (pl
      • show
      • similar
      • simple
      • +
      • sin
      • +
      • sinh
      • +
      • size
      • skip
      • smallint
      • snapshot
      • some
      • +
      • sort_direction
      • +
      • source
      • +
      • space
      • +
      • specific
      • +
      • specific_name
      • +
      • specifictype
      • sql
      • +
      • sqlcode
      • +
      • sqlerror
      • +
      • sqlexception
      • +
      • sqlstate
      • +
      • sqlwarning
      • +
      • sqrt
      • stable
      • standalone
      • start
      • +
      • state
      • statement
      • +
      • static
      • statistics
      • +
      • stddev_pop
      • +
      • stddev_samp
      • stdin
      • stdout
      • storage
      • +
      • stored
      • strict
      • +
      • string
      • strip
      • +
      • structure
      • +
      • style
      • +
      • subclass_origin
      • +
      • submultiset
      • subscription
      • +
      • subset
      • substring
      • +
      • substring_regex
      • +
      • succeeds
      • +
      • sum
      • +
      • support
      • symmetric
      • sysid
      • system
      • +
      • system_time
      • +
      • system_user
      • +
      • t
      • table
      • +
      • table_name
      • tables
      • tablesample
      • tablespace
      • +
      • tan
      • +
      • tanh
      • +
      • target
      • temp
      • template
      • temporary
      • text
      • then
      • +
      • through
      • ties
      • time
      • timestamp
      • +
      • timezone_hour
      • +
      • timezone_minute
      • to
      • +
      • token
      • +
      • top_level_count
      • trailing
      • transaction
      • +
      • transaction_active
      • +
      • transactions_committed
      • +
      • transactions_rolled_back
      • transform
      • +
      • transforms
      • +
      • translate
      • +
      • translate_regex
      • +
      • translation
      • treat
      • trigger
      • +
      • trigger_catalog
      • +
      • trigger_name
      • +
      • trigger_schema
      • trim
      • +
      • trim_array
      • true
      • truncate
      • trusted
      • type
      • types
      • +
      • uescape
      • unbounded
      • uncommitted
      • +
      • unconditional
      • +
      • under
      • unencrypted
      • union
      • unique
      • unknown
      • +
      • unlink
      • unlisten
      • unlogged
      • +
      • unmatched
      • +
      • unnamed
      • +
      • unnest
      • until
      • +
      • untyped
      • update
      • +
      • upper
      • +
      • uri
      • +
      • usage
      • user
      • +
      • user_defined_type_catalog
      • +
      • user_defined_type_code
      • +
      • user_defined_type_name
      • +
      • user_defined_type_schema
      • using
      • +
      • utf16
      • +
      • utf32
      • +
      • utf8
      • vacuum
      • valid
      • validate
      • validator
      • value
      • +
      • value_of
      • values
      • +
      • var_pop
      • +
      • var_samp
      • +
      • varbinary
      • varchar
      • variadic
      • varying
      • verbose
      • version
      • +
      • versioning
      • view
      • views
      • volatile
      • when
      • +
      • whenever
      • where
      • whitespace
      • +
      • width_bucket
      • window
      • with
      • within
      • @@ -497,17 +884,28 @@ These options may be applied as additional-properties (cli) or configOptions (pl
      • wrapper
      • write
      • xml
      • +
      • xmlagg
      • xmlattributes
      • +
      • xmlbinary
      • +
      • xmlcast
      • +
      • xmlcomment
      • xmlconcat
      • +
      • xmldeclaration
      • +
      • xmldocument
      • xmlelement
      • xmlexists
      • xmlforest
      • +
      • xmliterate
      • xmlnamespaces
      • xmlparse
      • xmlpi
      • +
      • xmlquery
      • xmlroot
      • +
      • xmlschema
      • xmlserialize
      • xmltable
      • +
      • xmltext
      • +
      • xmlvalidate
      • year
      • yes
      • zone
      • diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java index cb666ed1acce..5910be7699ab 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PostgresqlSchemaCodegen.java @@ -24,7 +24,6 @@ import org.openapitools.codegen.meta.features.*; import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.ModelsMap; -import org.openapitools.codegen.utils.ProcessUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.commons.lang3.StringUtils; @@ -50,7 +49,8 @@ public class PostgresqlSchemaCodegen extends DefaultCodegen { public static final Integer IDENTIFIER_MAX_LENGTH = 63; protected Vector postgresqlNumericTypes = new Vector<>(Arrays.asList( - "SMALLINT", "INTEGER", "BIGINT", "DECIMAL", "NUMERIC", "REAL", "DOUBLE PRECISION", "SMALLSERIAL", "SERIAL", + "SMALLINT", "INTEGER", "INT", "BIGINT", "DECIMAL", "NUMERIC", "REAL", "DOUBLE PRECISION", "SMALLSERIAL", + "SERIAL", "BIGSERIAL")); protected Vector postgresqlDateAndTimeTypes = new Vector<>(Arrays.asList( @@ -87,7 +87,7 @@ public class PostgresqlSchemaCodegen extends DefaultCodegen { * Returns identifier naming convention for table names and column names. */ @Getter - protected String identifierNamingConvention = "original"; + protected String identifierNamingConvention = "snake_case"; /** * Whether autoincrement feature enabled for integer 'id' fields */ @@ -122,63 +122,117 @@ public PostgresqlSchemaCodegen() { modelTemplateFiles.put("query_examples.mustache", ".sql"); // https://www.postgresql.org/docs/17/sql-keywords-appendix.html - setReservedWordsLowerCase( - Arrays.asList( - // SQL reserved words - "ABORT", "ABSOLUTE", "ACCESS", "ACTION", "ADD", "ADMIN", "AFTER", "AGGREGATE", "ALL", - "ALSO", "ALTER", "ALWAYS", "ANALYSE", "ANALYZE", "AND", "ANY", "ARRAY", "AS", "ASC", - "ASSERTION", "ASSIGNMENT", "ASYMMETRIC", "AT", "ATTACH", "ATTRIBUTE", "AUTHORIZATION", - "BACKWARD", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BIT", "BOOLEAN", "BOTH", - "BY", "CACHE", "CALL", "CALLED", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", "CHAIN", - "CHAR", "CHARACTER", "CHARACTERISTICS", "CHECK", "CHECKPOINT", "CLASS", "CLOSE", "CLUSTER", - "COALESCE", "COLLATE", "COLLATION", "COLUMN", "COLUMNS", "COMMENT", "COMMENTS", "COMMIT", - "COMMITTED", "CONCURRENTLY", "CONFIGURATION", "CONFLICT", "CONNECTION", "CONSTRAINT", - "CONSTRAINTS", "CONTENT", "CONTINUE", "CONVERSION", "COPY", "COST", "CREATE", "CROSS", - "CSV", "CUBE", "CURRENT", "CURRENT_CATALOG", "CURRENT_DATE", "CURRENT_ROLE", - "CURRENT_SCHEMA", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "CYCLE", - "DATA", "DATABASE", "DAY", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULTS", - "DEFERRABLE", "DEFERRED", "DEFINE", "DEFINER", "DELETE", "DELIMITER", "DELIMITERS", - "DEPENDS", "DEPTH", "DESC", "DESCRIBE", "DETACH", "DICTIONARY", "DISABLE", "DISCARD", - "DISTINCT", "DO", "DOCUMENT", "DOMAIN", "DOUBLE", "DROP", "EACH", "ELSE", "ENABLE", - "ENCODING", "ENCRYPTED", "END", "ENUM", "ESCAPE", "EVENT", "EXCEPT", "EXCLUDE", "EXCLUDING", - "EXCLUSIVE", "EXECUTE", "EXISTS", "EXPLAIN", "EXTENSION", "EXTERNAL", "EXTRACT", "FALSE", - "FAMILY", "FETCH", "FILTER", "FINALIZE", "FIRST", "FLOAT", "FOLLOWING", "FOR", "FORCE", - "FOREIGN", "FORWARD", "FREEZE", "FROM", "FULL", "FUNCTION", "FUNCTIONS", "GENERATED", - "GLOBAL", "GRANT", "GRANTED", "GREATEST", "GROUP", "GROUPING", "HANDLER", "HAVING", "HEADER", - "HOLD", "HOUR", "IDENTITY", "IF", "ILIKE", "IMMEDIATE", "IMMUTABLE", "IMPLICIT", "IMPORT", - "IN", "INCLUDING", "INCREMENT", "INDEX", "INDEXES", "INHERIT", "INHERITS", "INITIALLY", - "INLINE", "INNER", "INOUT", "INPUT", "INSENSITIVE", "INSERT", "INSTEAD", "INT", "INTEGER", - "INTERSECT", "INTERVAL", "INTO", "INVOKER", "IS", "ISNULL", "ISOLATION", "JOIN", "KEY", - "LABEL", "LANGUAGE", "LARGE", "LAST", "LATERAL", "LEADING", "LEAKPROOF", "LEAST", "LEFT", - "LEVEL", "LIKE", "LIMIT", "LISTEN", "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCATION", - "LOCK", "LOCKED", "LOGGED", "MAPPING", "MATCH", "MATERIALIZED", "MAXVALUE", "METHOD", "MINUTE", - "MINVALUE", "MODE", "MODIFY", "MONTH", "MOVE", "NAME", "NAMES", "NATIONAL", "NATURAL", "NCHAR", - "NEW", "NEXT", "NO", "NONE", "NORMALIZE", "NORMALIZED", "NOT", "NOTHING", "NOTIFY", "NOTNULL", - "NOWAIT", "NULL", "NULLABLE", "NULLIF", "NUMERIC", "OBJECT", "OF", "OFF", "OFFSET", "OIDS", - "OLD", "ON", "ONLY", "OPERATOR", - "OPTION", "OPTIONS", "OR", "ORDER", "ORDINALITY", "OTHERS", "OUT", "OUTER", "OVER", "OVERLAPS", - "OVERLAY", "OVERRIDING", "OWNED", "OWNER", "PARALLEL", "PARAMETER", "PARSER", "PARTIAL", - "PARTITION", "PASSING", "PASSWORD", "PLACING", "PLANS", "POLICY", "POSITION", "PRECEDING", - "PRECISION", "PREPARE", "PREPARED", "PRESERVE", "PRIMARY", "PRIOR", "PRIVILEGES", "PROCEDURAL", - "PROCEDURE", "PROGRAM", "PUBLICATION", "QUOTE", "RANGE", "READ", "REAL", "REASSIGN", "RECHECK", - "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "REFRESH", "REINDEX", "RELATIVE", "RELEASE", - "RENAME", "REPEATABLE", "REPLACE", "REPLICA", "RESET", "RESTART", "RESTRICT", "RETURNING", - "RETURNS", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "ROLLUP", "ROUTINE", "ROUTINES", "ROW", - "ROWS", "RULE", "SAVEPOINT", "SCHEMA", "SCHEMAS", "SCROLL", "SEARCH", "SECOND", "SECURITY", - "SELECT", "SEQUENCE", "SEQUENCES", "SERIALIZABLE", "SERVER", "SESSION", "SESSION_USER", - "SET", "SETOF", "SETS", "SHARE", "SHOW", "SIMILAR", "SIMPLE", "SKIP", "SMALLINT", "SNAPSHOT", - "SOME", "SQL", "STABLE", "STANDALONE", "START", "STATEMENT", "STATISTICS", "STDIN", "STDOUT", - "STORAGE", "STRICT", "STRIP", "SUBSCRIPTION", "SUBSTRING", "SYMMETRIC", "SYSID", "SYSTEM", - "TABLE", "TABLES", "TABLESAMPLE", "TABLESPACE", "TEMP", "TEMPLATE", "TEMPORARY", "TEXT", - "THEN", "TIES", "TIME", "TIMESTAMP", "TO", "TRAILING", "TRANSACTION", "TRANSFORM", "TREAT", - "TRIGGER", "TRIM", "TRUE", "TRUNCATE", "TRUSTED", "TYPE", "TYPES", "UNBOUNDED", "UNCOMMITTED", - "UNENCRYPTED", "UNION", "UNIQUE", "UNKNOWN", "UNLISTEN", "UNLOGGED", "UNTIL", "UPDATE", - "USER", "USING", "VACUUM", "VALID", "VALIDATE", "VALIDATOR", "VALUE", "VALUES", "VARCHAR", - "VARIADIC", "VARYING", "VERBOSE", "VERSION", "VIEW", "VIEWS", "VOLATILE", "WHEN", "WHERE", - "WHITESPACE", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WORK", "WRAPPER", "WRITE", "XML", - "XMLATTRIBUTES", "XMLCONCAT", "XMLELEMENT", "XMLEXISTS", "XMLFOREST", "XMLNAMESPACES", - "XMLPARSE", "XMLPI", "XMLROOT", "XMLSERIALIZE", "XMLTABLE", "YEAR", "YES", "ZONE" - )); + setReservedWordsLowerCase( + Arrays.asList( + // SQL reserved words + "A", "ABORT", "ABS", "ABSENT", "ABSOLUTE", "ACCESS", "ACCORDING", "ACOS", "ACTION", "ADA", + "ADD", "ADMIN", "AFTER", "AGGREGATE", "ALL", "ALLOCATE", "ALSO", "ALTER", "ALWAYS", "ANALYSE", + "ANALYZE", "AND", "ANY", "ANY_VALUE", "ARE", "ARRAY", "ARRAY_AGG", "ARRAY_MAX_CARDINALITY", + "AS", "ASC", "ASENSITIVE", "ASIN", "ASSERTION", "ASSIGNMENT", "ASYMMETRIC", "AT", "ATAN", + "ATOMIC", "ATTACH", "ATTRIBUTE", "ATTRIBUTES", "AUTHORIZATION", "AVG", "BACKWARD", "BASE64", + "BEFORE", "BEGIN", "BEGIN_FRAME", "BEGIN_PARTITION", "BERNOULLI", "BETWEEN", "BIGINT", "BINARY", + "BIT", "BIT_LENGTH", "BLOB", "BLOCKED", "BOM", "BOOLEAN", "BOTH", "BREADTH", "BTRIM", "BY", "C", + "CACHE", "CALL", "CALLED", "CARDINALITY", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", + "CATALOG_NAME", "CEIL", "CEILING", "CHAIN", "CHAINING", "CHAR", "CHARACTER", "CHARACTERISTICS", + "CHARACTERS", "CHARACTER_LENGTH", "CHARACTER_SET_CATALOG", "CHARACTER_SET_NAME", + "CHARACTER_SET_SCHEMA", "CHAR_LENGTH", "CHECK", "CHECKPOINT", "CLASS", "CLASSIFIER", + "CLASS_ORIGIN", "CLOB", "CLOSE", "CLUSTER", "COALESCE", "COBOL", "COLLATE", "COLLATION", + "COLLATION_CATALOG", "COLLATION_NAME", "COLLATION_SCHEMA", "COLLECT", "COLUMN", "COLUMNS", + "COLUMN_NAME", "COMMAND_FUNCTION", "COMMAND_FUNCTION_CODE", "COMMENT", "COMMENTS", "COMMIT", + "COMMITTED", "COMPRESSION", "CONCURRENTLY", "CONDITION", "CONDITIONAL", "CONDITION_NUMBER", + "CONFIGURATION", "CONFLICT", "CONNECT", "CONNECTION", "CONNECTION_NAME", "CONSTRAINT", + "CONSTRAINTS", "CONSTRAINT_CATALOG", "CONSTRAINT_NAME", "CONSTRAINT_SCHEMA", "CONSTRUCTOR", + "CONTAINS", "CONTENT", "CONTINUE", "CONTROL", "CONVERSION", "CONVERT", "COPARTITION", "COPY", + "CORR", "CORRESPONDING", "COS", "COSH", "COST", "COUNT", "COVAR_POP", "COVAR_SAMP", "CREATE", + "CROSS", "CSV", "CUBE", "CUME_DIST", "CURRENT", "CURRENT_CATALOG", "CURRENT_DATE", + "CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_PATH", "CURRENT_ROLE", "CURRENT_ROW", + "CURRENT_SCHEMA", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_TRANSFORM_GROUP_FOR_TYPE", + "CURRENT_USER", "CURSOR", "CURSOR_NAME", "CYCLE", "DATA", "DATABASE", "DATALINK", "DATE", + "DATETIME_INTERVAL_CODE", "DATETIME_INTERVAL_PRECISION", "DAY", "DB", "DEALLOCATE", "DEC", + "DECFLOAT", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULTS", "DEFERRABLE", "DEFERRED", "DEFINE", + "DEFINED", "DEFINER", "DEGREE", "DELETE", "DELIMITER", "DELIMITERS", "DENSE_RANK", "DEPENDS", + "DEPTH", "DEREF", "DERIVED", "DESC", "DESCRIBE", "DESCRIPTOR", "DETACH", "DETERMINISTIC", + "DIAGNOSTICS", "DICTIONARY", "DISABLE", "DISCARD", "DISCONNECT", "DISPATCH", "DISTINCT", + "DLNEWCOPY", "DLPREVIOUSCOPY", "DLURLCOMPLETE", "DLURLCOMPLETEONLY", "DLURLCOMPLETEWRITE", + "DLURLPATH", "DLURLPATHONLY", "DLURLPATHWRITE", "DLURLSCHEME", "DLURLSERVER", "DLVALUE", "DO", + "DOCUMENT", "DOMAIN", "DOUBLE", "DROP", "DYNAMIC", "DYNAMIC_FUNCTION", "DYNAMIC_FUNCTION_CODE", + "EACH", "ELEMENT", "ELSE", "EMPTY", "ENABLE", "ENCODING", "ENCRYPTED", "END", "END-EXEC", + "END_FRAME", "END_PARTITION", "ENFORCED", "ENUM", "EQUALS", "ERROR", "ESCAPE", "EVENT", "EVERY", + "EXCEPT", "EXCEPTION", "EXCLUDE", "EXCLUDING", "EXCLUSIVE", "EXEC", "EXECUTE", "EXISTS", "EXP", + "EXPLAIN", "EXPRESSION", "EXTENSION", "EXTERNAL", "EXTRACT", "FALSE", "FAMILY", "FETCH", "FILE", + "FILTER", "FINAL", "FINALIZE", "FINISH", "FIRST", "FIRST_VALUE", "FLAG", "FLOAT", "FLOOR", + "FOLLOWING", "FOR", "FORCE", "FOREIGN", "FORMAT", "FORTRAN", "FORWARD", "FOUND", "FRAME_ROW", + "FREE", "FREEZE", "FROM", "FS", "FULFILL", "FULL", "FUNCTION", "FUNCTIONS", "FUSION", "G", + "GENERAL", "GENERATED", "GET", "GLOBAL", "GO", "GOTO", "GRANT", "GRANTED", "GREATEST", "GROUP", + "GROUPING", "GROUPS", "HANDLER", "HAVING", "HEADER", "HEX", "HIERARCHY", "HOLD", "HOUR", "ID", + "IDENTITY", "IF", "IGNORE", "ILIKE", "IMMEDIATE", "IMMEDIATELY", "IMMUTABLE", "IMPLEMENTATION", + "IMPLICIT", "IMPORT", "IN", "INCLUDE", "INCLUDING", "INCREMENT", "INDENT", "INDEX", "INDEXES", + "INDICATOR", "INHERIT", "INHERITS", "INITIAL", "INITIALLY", "INLINE", "INNER", "INOUT", "INPUT", + "INSENSITIVE", "INSERT", "INSTANCE", "INSTANTIABLE", "INSTEAD", "INT", "INTEGER", "INTEGRITY", + "INTERSECT", "INTERSECTION", "INTERVAL", "INTO", "INVOKER", "IS", "ISNULL", "ISOLATION", "JOIN", + "JSON", "JSON_ARRAY", "JSON_ARRAYAGG", "JSON_EXISTS", "JSON_OBJECT", "JSON_OBJECTAGG", + "JSON_QUERY", "JSON_SCALAR", "JSON_SERIALIZE", "JSON_TABLE", "JSON_TABLE_PRIMITIVE", + "JSON_VALUE", "K", "KEEP", "KEY", "KEYS", "KEY_MEMBER", "KEY_TYPE", "LABEL", "LAG", "LANGUAGE", + "LARGE", "LAST", "LAST_VALUE", "LATERAL", "LEAD", "LEADING", "LEAKPROOF", "LEAST", "LEFT", + "LENGTH", "LEVEL", "LIBRARY", "LIKE", "LIKE_REGEX", "LIMIT", "LINK", "LISTAGG", "LISTEN", "LN", + "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCATION", "LOCATOR", "LOCK", "LOCKED", "LOG", + "LOG10", "LOGGED", "LOWER", "LPAD", "LTRIM", "M", "MAP", "MAPPING", "MATCH", "MATCHED", + "MATCHES", "MATCH_NUMBER", "MATCH_RECOGNIZE", "MATERIALIZED", "MAX", "MAXVALUE", "MEASURES", + "MEMBER", "MERGE", "MERGE_ACTION", "MESSAGE_LENGTH", "MESSAGE_OCTET_LENGTH", "MESSAGE_TEXT", + "METHOD", "MIN", "MINUTE", "MINVALUE", "MOD", "MODE", "MODIFIES", "MODULE", "MONTH", "MORE", + "MOVE", "MULTISET", "MUMPS", "NAME", "NAMES", "NAMESPACE", "NATIONAL", "NATURAL", "NCHAR", + "NCLOB", "NESTED", "NESTING", "NEW", "NEXT", "NFC", "NFD", "NFKC", "NFKD", "NIL", "NO", "NONE", + "NORMALIZE", "NORMALIZED", "NOT", "NOTHING", "NOTIFY", "NOTNULL", "NOWAIT", "NTH_VALUE", + "NTILE", "NULL", "NULLABLE", "NULLIF", "NULLS", "NULL_ORDERING", "NUMBER", "NUMERIC", "OBJECT", + "OCCURRENCE", "OCCURRENCES_REGEX", "OCTETS", "OCTET_LENGTH", "OF", "OFF", "OFFSET", "OIDS", + "OLD", "OMIT", "ON", "ONE", "ONLY", "OPEN", "OPERATOR", "OPTION", "OPTIONS", "OR", "ORDER", + "ORDERING", "ORDINALITY", "OTHERS", "OUT", "OUTER", "OUTPUT", "OVER", "OVERFLOW", "OVERLAPS", + "OVERLAY", "OVERRIDING", "OWNED", "OWNER", "P", "PAD", "PARALLEL", "PARAMETER", + "PARAMETER_MODE", "PARAMETER_NAME", "PARAMETER_ORDINAL_POSITION", "PARAMETER_SPECIFIC_CATALOG", + "PARAMETER_SPECIFIC_NAME", "PARAMETER_SPECIFIC_SCHEMA", "PARSER", "PARTIAL", "PARTITION", + "PASCAL", "PASS", "PASSING", "PASSTHROUGH", "PASSWORD", "PAST", "PATH", "PATTERN", "PER", + "PERCENT", "PERCENTILE_CONT", "PERCENTILE_DISC", "PERCENT_RANK", "PERIOD", "PERMISSION", + "PERMUTE", "PIPE", "PLACING", "PLAN", "PLANS", "PLI", "POLICY", "PORTION", "POSITION", + "POSITION_REGEX", "POWER", "PRECEDES", "PRECEDING", "PRECISION", "PREPARE", "PREPARED", + "PRESERVE", "PREV", "PRIMARY", "PRIOR", "PRIVATE", "PRIVILEGES", "PROCEDURAL", "PROCEDURE", + "PROCEDURES", "PROGRAM", "PRUNE", "PTF", "PUBLIC", "PUBLICATION", "QUOTE", "QUOTES", "RANGE", + "RANK", "READ", "READS", "REAL", "REASSIGN", "RECHECK", "RECOVERY", "RECURSIVE", "REF", + "REFERENCES", "REFERENCING", "REFRESH", "REGR_AVGX", "REGR_AVGY", "REGR_COUNT", + "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE", "REGR_SXX", "REGR_SXY", "REGR_SYY", "REINDEX", + "RELATIVE", "RELEASE", "RENAME", "REPEATABLE", "REPLACE", "REPLICA", "REQUIRING", "RESET", + "RESPECT", "RESTART", "RESTORE", "RESTRICT", "RESULT", "RETURN", "RETURNED_CARDINALITY", + "RETURNED_LENGTH", "RETURNED_OCTET_LENGTH", "RETURNED_SQLSTATE", "RETURNING", "RETURNS", + "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "ROLLUP", "ROUTINE", "ROUTINES", "ROUTINE_CATALOG", + "ROUTINE_NAME", "ROUTINE_SCHEMA", "ROW", "ROWS", "ROW_COUNT", "ROW_NUMBER", "RPAD", "RTRIM", + "RULE", "RUNNING", "SAVEPOINT", "SCALAR", "SCALE", "SCHEMA", "SCHEMAS", "SCHEMA_NAME", "SCOPE", + "SCOPE_CATALOG", "SCOPE_NAME", "SCOPE_SCHEMA", "SCROLL", "SEARCH", "SECOND", "SECTION", + "SECURITY", "SEEK", "SELECT", "SELECTIVE", "SELF", "SEMANTICS", "SENSITIVE", "SEQUENCE", + "SEQUENCES", "SERIALIZABLE", "SERVER", "SERVER_NAME", "SESSION", "SESSION_USER", "SET", "SETOF", + "SETS", "SHARE", "SHOW", "SIMILAR", "SIMPLE", "SIN", "SINH", "SIZE", "SKIP", "SMALLINT", + "SNAPSHOT", "SOME", "SORT_DIRECTION", "SOURCE", "SPACE", "SPECIFIC", "SPECIFICTYPE", + "SPECIFIC_NAME", "SQL", "SQLCODE", "SQLERROR", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "SQRT", + "STABLE", "STANDALONE", "START", "STATE", "STATEMENT", "STATIC", "STATISTICS", "STDDEV_POP", + "STDDEV_SAMP", "STDIN", "STDOUT", "STORAGE", "STORED", "STRICT", "STRING", "STRIP", "STRUCTURE", + "STYLE", "SUBCLASS_ORIGIN", "SUBMULTISET", "SUBSCRIPTION", "SUBSET", "SUBSTRING", + "SUBSTRING_REGEX", "SUCCEEDS", "SUM", "SUPPORT", "SYMMETRIC", "SYSID", "SYSTEM", "SYSTEM_TIME", + "SYSTEM_USER", "T", "TABLE", "TABLES", "TABLESAMPLE", "TABLESPACE", "TABLE_NAME", "TAN", "TANH", + "TARGET", "TEMP", "TEMPLATE", "TEMPORARY", "TEXT", "THEN", "THROUGH", "TIES", "TIME", + "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TOKEN", "TOP_LEVEL_COUNT", "TRAILING", + "TRANSACTION", "TRANSACTIONS_COMMITTED", "TRANSACTIONS_ROLLED_BACK", "TRANSACTION_ACTIVE", + "TRANSFORM", "TRANSFORMS", "TRANSLATE", "TRANSLATE_REGEX", "TRANSLATION", "TREAT", "TRIGGER", + "TRIGGER_CATALOG", "TRIGGER_NAME", "TRIGGER_SCHEMA", "TRIM", "TRIM_ARRAY", "TRUE", "TRUNCATE", + "TRUSTED", "TYPE", "TYPES", "UESCAPE", "UNBOUNDED", "UNCOMMITTED", "UNCONDITIONAL", "UNDER", + "UNENCRYPTED", "UNION", "UNIQUE", "UNKNOWN", "UNLINK", "UNLISTEN", "UNLOGGED", "UNMATCHED", + "UNNAMED", "UNNEST", "UNTIL", "UNTYPED", "UPDATE", "UPPER", "URI", "USAGE", "USER", + "USER_DEFINED_TYPE_CATALOG", "USER_DEFINED_TYPE_CODE", "USER_DEFINED_TYPE_NAME", + "USER_DEFINED_TYPE_SCHEMA", "USING", "UTF16", "UTF32", "UTF8", "VACUUM", "VALID", "VALIDATE", + "VALIDATOR", "VALUE", "VALUES", "VALUE_OF", "VARBINARY", "VARCHAR", "VARIADIC", "VARYING", + "VAR_POP", "VAR_SAMP", "VERBOSE", "VERSION", "VERSIONING", "VIEW", "VIEWS", "VOLATILE", "WHEN", + "WHENEVER", "WHERE", "WHITESPACE", "WIDTH_BUCKET", "WINDOW", "WITH", "WITHIN", "WITHOUT", + "WORK", "WRAPPER", "WRITE", "XML", "XMLAGG", "XMLATTRIBUTES", "XMLBINARY", "XMLCAST", + "XMLCOMMENT", "XMLCONCAT", "XMLDECLARATION", "XMLDOCUMENT", "XMLELEMENT", "XMLEXISTS", + "XMLFOREST", "XMLITERATE", "XMLNAMESPACES", "XMLPARSE", "XMLPI", "XMLQUERY", "XMLROOT", + "XMLSCHEMA", "XMLSERIALIZE", "XMLTABLE", "XMLTEXT", "XMLVALIDATE", "YEAR", "YES", "ZONE")); // primitive data types languageSpecificPrimitives = new HashSet<>( @@ -204,9 +258,7 @@ public PostgresqlSchemaCodegen() { "mixed", "number", "void", - "byte" - ) - ); + "byte")); // https://www.postgresql.org/docs/17/datatype.html typeMapping.put("array", "JSON"); @@ -215,7 +267,7 @@ public PostgresqlSchemaCodegen() { typeMapping.put("List", "JSON"); typeMapping.put("boolean", "BOOLEAN"); typeMapping.put("string", "TEXT"); - typeMapping.put("int", "INT"); + typeMapping.put("int", "INTEGER"); typeMapping.put("byte", "TEXT"); typeMapping.put("float", "DECIMAL"); typeMapping.put("number", "DECIMAL"); @@ -226,7 +278,7 @@ public PostgresqlSchemaCodegen() { typeMapping.put("char", "TEXT"); typeMapping.put("double", "DECIMAL"); typeMapping.put("object", "JSON"); - typeMapping.put("integer", "INT"); + typeMapping.put("integer", "INTEGER"); typeMapping.put("ByteArray", "BYTEA"); typeMapping.put("file", "BYTEA"); typeMapping.put("UUID", "TEXT"); @@ -237,28 +289,34 @@ public PostgresqlSchemaCodegen() { // it seems that cli options from DefaultCodegen are useless here cliOptions.clear(); - addOption(DEFAULT_DATABASE_NAME, - "Default database name for all PostgreSQL queries", defaultDatabaseName); + + addOption(DEFAULT_DATABASE_NAME, + "Database name that will be used for all generated PostgreSQL DDL and DML statements.", defaultDatabaseName); + addSwitch(NAMED_PARAMETERS_ENABLED, - "Generates model prepared SQLs with named parameters, eg. :petName. Question mark placeholder used when option disabled.", + "Generates query examples with named variables in value placeholders (eg.`:name`,`:quantity`) if `true`. Otherwise, generates question marks `?` in value placeholders.", namedParametersEnabled); - addOption(JSON_DATA_TYPE, - "Use special JSON PostgreSQL data type for complex model properties.", - jsonDataType); + addSwitch(ID_AUTOINC_ENABLED, - "Generates PostgreSQL sequences for autoincrement feature for integer 'id' fields.", + "If `true`, generates autoincrement PostgreSQL types `SERIAL` and `BIGSERIAL` for `int32` and `int64` respectively for integer fields with name 'id'.", idAutoIncEnabled); - + // we used to snake_case table/column names, let's add this option CliOption identifierNamingOpt = new CliOption(IDENTIFIER_NAMING_CONVENTION, - "Naming convention of PostgreSQL identifiers(table names and column names). This is not related to database name which is defined by " - + DEFAULT_DATABASE_NAME + " option"); - - identifierNamingOpt.addEnum("original", "Do not transform original names") - .addEnum("snake_case", "Use snake_case names") - .setDefault("original"); - + "Naming convention of PostgreSQL idebntifiers (table names and column names)."); + identifierNamingOpt.addEnum("snake_case", "Transform named to 'snake_case'.") + .addEnum("original", "Leave original names as in `YAML` file.") + .setDefault("snake_case"); cliOptions.add(identifierNamingOpt); + + CliOption jsonDataTypeOpt = new CliOption(JSON_DATA_TYPE, + "Use of PostgreSQL data types for complex model properties."); + jsonDataTypeOpt.addEnum("json", "Generate `JSON` fields. Value is stored in `JSON` data type field as human-readable text. Value compliance with JSON standard is checked.") + .addEnum("jsonb", + "Generate `JSONB` fields. Value is stored in `JSONB` data type field in binary format. `JSONB` data type is generally nore efficient than `JSON` but it is not human-readable. Value compliance with JSON standard is checked.") + .addEnum("off", "Generate `TEXT` fields. Just store the value as plain text. Value compliance with JSON standard is not checked.") + .setDefault("json"); + cliOptions.add(jsonDataTypeOpt); } @Override @@ -290,14 +348,6 @@ public void processOpts() { } } - if (additionalProperties.containsKey(JSON_DATA_TYPE)) { - if (additionalProperties.get(JSON_DATA_TYPE).equals("off")) { - this.setJsonDataType("off"); - } else if (additionalProperties.get(JSON_DATA_TYPE).equals("jsonb")) { - this.setJsonDataType("jsonb"); - } - } - if (additionalProperties.containsKey(NAMED_PARAMETERS_ENABLED)) { this.setNamedParametersEnabled( Boolean.valueOf(additionalProperties.get(NAMED_PARAMETERS_ENABLED).toString())); @@ -314,13 +364,19 @@ public void processOpts() { this.setIdentifierNamingConvention((String) additionalProperties.get(IDENTIFIER_NAMING_CONVENTION)); } + if (additionalProperties.containsKey(JSON_DATA_TYPE)) { + this.setJsonDataType((String) additionalProperties.get(JSON_DATA_TYPE)); + } + // make model src path available in mustache template additionalProperties.put("modelSrcPath", "./" + toSrcPath(modelPackage)); - supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); - supportingFiles.add(new SupportingFile("postgresql_schema.mustache", "", "postgresql_schema.sql")); - supportingFiles - .add(new SupportingFile("postgresql_schema_oauth2.mustache", "", "postgresql_schema_oauth2.sql")); + supportingFiles.add(new SupportingFile( + "README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile( + "postgresql_schema.mustache", "", "postgresql_schema.sql")); + supportingFiles.add(new SupportingFile( + "postgresql_schema_oauth2.mustache", "", "postgresql_schema_oauth2.sql")); } @Override @@ -351,7 +407,7 @@ public ModelsMap postProcessModels(ModelsMap objs) { postgresqlSchema.put("tableDefinition", tableDefinition); tableDefinition.put("tblName", tableName); tableDefinition.put("tblComment", modelDescription); - if (isReservedWord(tableName)) { // Output table name in double quotes if it is a reserved word + if (isReservedWord(tableName)) { // Output table name in double quotes if it is a reserved word tableDefinition.put("tblNameQuoted", true); } } @@ -366,7 +422,7 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert processBooleanTypeProperty(model, property); break; case "SMALLINT": - case "INT": + case "INTEGER": case "BIGINT": processIntegerTypeProperty(model, property); break; @@ -416,6 +472,7 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope Boolean required = property.getRequired(); Boolean isUuid = property.isUuid; Boolean isEnum = property.isEnum; + String tableName = this.toTableName(model.getName()); if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { // user already specified schema values @@ -433,13 +490,14 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); - if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word columnDefinition.put("colNameQuoted", true); } else { columnDefinition.put("colNameQuoted", false); } - columnDefinition.put("tblName", model.getName()); - if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblName", tableName); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a + // reserved word columnDefinition.put("tblNameQuoted", true); } else { columnDefinition.put("tblNameQuoted", false); @@ -448,7 +506,8 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope if (Boolean.TRUE.equals(isEnum)) { Map allowableValues = property.getAllowableValues(); List enumValues = (List) allowableValues.get("values"); - String typeName = model.getName() + "_" + property.getName(); + String typeName = this.toTableName(model.getName()) + + "_" + this.toColumnName(property.getName()); postgresqlSchema.put("typeDefinition", typeDefinition); columnDefinition.put("colDataType", typeName); typeDefinition.put("typeName", typeName); @@ -487,15 +546,17 @@ public void processIntegerTypeProperty(CodegenModel model, CodegenProperty prope } } - if ( !columnDefinition.get("colDataType").equals("SERIAL") - && !columnDefinition.get("colDataType").equals("BIGSERIAL")) { // No default value for autoincremented IDs + if (!columnDefinition.get("colDataType").equals("SERIAL") + && !columnDefinition.get("colDataType").equals("BIGSERIAL")) { // No default value for autoincremented + // IDs if (Boolean.TRUE.equals(required)) { columnDefinition.put("colNotNull", true); } else { columnDefinition.put("colNotNull", false); try { columnDefinition.put("colDefault", - toCodegenPostgresqlDataTypeDefault(defaultValue, (String) columnDefinition.get("colDataType"))); + toCodegenPostgresqlDataTypeDefault(defaultValue, + (String) columnDefinition.get("colDataType"))); } catch (RuntimeException exception) { LOGGER.warn( "Property '{}' of model '{}' mapped to PostgreSQL data type which doesn't support default value", @@ -535,6 +596,7 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope String defaultValue = property.getDefaultValue(); Boolean required = property.getRequired(); Boolean isEnum = property.isEnum; + String tableName = this.toTableName(model.getName()); if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { // user already specified schema values @@ -552,13 +614,14 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); - if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word columnDefinition.put("colNameQuoted", true); } else { columnDefinition.put("colNameQuoted", false); } - columnDefinition.put("tblName", model.getName()); - if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblName", tableName); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a + // reserved word columnDefinition.put("tblNameQuoted", true); } else { columnDefinition.put("tblNameQuoted", false); @@ -567,7 +630,8 @@ public void processDecimalTypeProperty(CodegenModel model, CodegenProperty prope if (Boolean.TRUE.equals(isEnum)) { Map allowableValues = property.getAllowableValues(); List enumValues = (List) allowableValues.get("values"); - String typeName = model.getName() + "_" + property.getName(); + String typeName = this.toTableName(model.getName()) + + "_" + this.toColumnName(property.getName()); postgresqlSchema.put("typeDefinition", typeDefinition); columnDefinition.put("colDataType", typeName); typeDefinition.put("typeName", typeName); @@ -638,6 +702,7 @@ public void processStringTypeProperty(CodegenModel model, CodegenProperty proper String defaultValue = property.getDefaultValue(); Boolean required = property.getRequired(); Boolean isEnum = property.isEnum; + String tableName = this.toTableName(model.getName()); if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { // user already specified schema values @@ -655,13 +720,14 @@ public void processStringTypeProperty(CodegenModel model, CodegenProperty proper vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); - if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word columnDefinition.put("colNameQuoted", true); } else { columnDefinition.put("colNameQuoted", false); } - columnDefinition.put("tblName", model.getName()); - if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblName", tableName); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a + // reserved word columnDefinition.put("tblNameQuoted", true); } else { columnDefinition.put("tblNameQuoted", false); @@ -670,7 +736,8 @@ public void processStringTypeProperty(CodegenModel model, CodegenProperty proper if (Boolean.TRUE.equals(isEnum)) { Map allowableValues = property.getAllowableValues(); List enumValues = (List) allowableValues.get("values"); - String typeName = model.getName() + "_" + property.getName(); + String typeName = this.toTableName(model.getName()) + + "_" + this.toColumnName(property.getName()); postgresqlSchema.put("typeDefinition", typeDefinition); columnDefinition.put("colDataType", typeName); typeDefinition.put("typeName", typeName); @@ -734,6 +801,7 @@ public void processBooleanTypeProperty(CodegenModel model, CodegenProperty prope String description = property.getDescription(); String defaultValue = property.getDefaultValue(); Boolean required = property.getRequired(); + String tableName = this.toTableName(model.getName()); if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { // user already specified schema values @@ -751,13 +819,14 @@ public void processBooleanTypeProperty(CodegenModel model, CodegenProperty prope vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); - if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word columnDefinition.put("colNameQuoted", true); } else { columnDefinition.put("colNameQuoted", false); } - columnDefinition.put("tblName", model.getName()); - if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblName", tableName); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a + // reserved word columnDefinition.put("tblNameQuoted", true); } else { columnDefinition.put("tblNameQuoted", false); @@ -802,6 +871,7 @@ public void processDateTypeProperty(CodegenModel model, CodegenProperty property Boolean required = property.getRequired(); String description = property.getDescription(); String defaultValue = property.getDefaultValue(); + String tableName = this.toTableName(model.getName()); if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { // user already specified schema values @@ -819,13 +889,14 @@ public void processDateTypeProperty(CodegenModel model, CodegenProperty property vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); - if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word columnDefinition.put("colNameQuoted", true); } else { columnDefinition.put("colNameQuoted", false); } - columnDefinition.put("tblName", model.getName()); - if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblName", tableName); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a + // reserved word columnDefinition.put("tblNameQuoted", true); } else { columnDefinition.put("tblNameQuoted", false); @@ -870,6 +941,7 @@ public void processJsonTypeProperty(CodegenModel model, CodegenProperty property Boolean required = property.getRequired(); String description = property.getDescription(); String defaultValue = property.getDefaultValue(); + String tableName = this.toTableName(model.getName()); if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { // user already specified schema values @@ -887,13 +959,14 @@ public void processJsonTypeProperty(CodegenModel model, CodegenProperty property vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); - if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word columnDefinition.put("colNameQuoted", true); } else { columnDefinition.put("colNameQuoted", false); } - columnDefinition.put("tblName", model.getName()); - if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblName", tableName); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a + // reserved word columnDefinition.put("tblNameQuoted", true); } else { columnDefinition.put("tblNameQuoted", false); @@ -946,6 +1019,7 @@ public void processUnknownTypeProperty(CodegenModel model, CodegenProperty prope Boolean required = property.getRequired(); String description = property.getDescription(); String defaultValue = property.getDefaultValue(); + String tableName = this.toTableName(model.getName()); if (vendorExtensions.containsKey(VENDOR_EXTENSION_POSTGRESQL_SCHEMA)) { // user already specified schema values @@ -963,13 +1037,14 @@ public void processUnknownTypeProperty(CodegenModel model, CodegenProperty prope vendorExtensions.put(VENDOR_EXTENSION_POSTGRESQL_SCHEMA, postgresqlSchema); postgresqlSchema.put("columnDefinition", columnDefinition); columnDefinition.put("colName", colName); - if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word + if (isReservedWord(colName)) { // Output column name in double quotes if it is a reserved word columnDefinition.put("colNameQuoted", true); } else { columnDefinition.put("colNameQuoted", false); } - columnDefinition.put("tblName", model.getName()); - if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a reserved word + columnDefinition.put("tblName", tableName); + if (isReservedWord(model.getName())) { // Output table name (for column comment) in double quotes if it is a + // reserved word columnDefinition.put("tblNameQuoted", true); } else { columnDefinition.put("tblNameQuoted", false); @@ -1048,7 +1123,7 @@ public HashMap toCodegenPostgresqlDataTypeDefault(String default switch (postgresqlDataType.toUpperCase(Locale.ROOT)) { case "SMALLINT": - case "INT": + case "INTEGER": case "BIGINT": // SERIAL DEFAULT VALUE is a special case. In the definition of an integer // column, it is an alias for NOT NULL AUTO_INCREMENT UNIQUE @@ -1081,7 +1156,7 @@ public HashMap toCodegenPostgresqlDataTypeDefault(String default defaultMap.put("isString", false); defaultMap.put("isNumeric", false); defaultMap.put("isKeyword", true); - + } else { defaultMap.put("defaultValue", defaultValue); defaultMap.put("isString", true); @@ -1131,11 +1206,11 @@ public String getPostgresqlMatchedIntegerDataType(Long minimum, Long maximum, Bo if (actualMin >= -32768 && actualMax <= 32767) { return "SMALLINT"; } else if (actualMin >= -2147483648 && actualMax <= 2147483647) { - return "INT"; + return "INTEGER"; } else if (actualMin < -2147483648 || actualMax > 2147483647) { return "BIGINT"; } - return "INT"; + return "INTEGER"; } /** @@ -1150,7 +1225,7 @@ public String getPostgresqlMatchedStringDataType(Integer minLength, Integer maxL // we can choose fit postgresql data type // ref: https://www.postgresql.org/docs/17/datatype-character.html int min = (minLength != null && minLength >= 0) ? minLength : 0; - int max = (maxLength != null && maxLength >= 0) ? maxLength : 65535; + int max = (maxLength != null && maxLength >= 0) ? maxLength : 65536; Integer actualMin = Math.min(min, max); // sometimes minLength and maxLength values can be mixed up Integer actualMax = Math.max(min, max); // sometimes only minLength specified and it can be pretty high if (minLength != null && maxLength != null && minLength > maxLength) { @@ -1173,7 +1248,7 @@ public Boolean isPostgresqlDataType(String dataType) { return (postgresqlNumericTypes.contains(dataType.toUpperCase(Locale.ROOT)) || postgresqlDateAndTimeTypes.contains(dataType.toUpperCase(Locale.ROOT)) || postgresqlStringTypes.contains(dataType.toUpperCase(Locale.ROOT)) || - postgresqlSpatialTypes.contains(dataType.toUpperCase(Locale.ROOT))) || + postgresqlSpatialTypes.contains(dataType.toUpperCase(Locale.ROOT))) || dataType.toUpperCase(Locale.ROOT).equals("JSON") || dataType.toUpperCase(Locale.ROOT).equals("JSONB"); } @@ -1187,7 +1262,8 @@ public Boolean isPostgresqlDataType(String dataType) { public String toDatabaseName(String name) { String identifier = toPostgresqlIdentifier(name, databaseNamePrefix, databaseNameSuffix); if (identifier.length() > IDENTIFIER_MAX_LENGTH) { - LOGGER.warn("Database name cannot exceed {} chars. Name '{}' will be truncated", IDENTIFIER_MAX_LENGTH, name); + LOGGER.warn("Database name cannot exceed {} chars. Name '{}' will be truncated", IDENTIFIER_MAX_LENGTH, + name); identifier = identifier.substring(0, IDENTIFIER_MAX_LENGTH); } return identifier; @@ -1313,9 +1389,16 @@ public String escapePostgresqlQuotedIdentifier(String identifier) { @Override public String escapeReservedWord(String name) { + // *** For PostgreSQL: + // *** If table name or column name is a reserved word, + // *** it could be still used in double quotes + // *** (this is done in template by adding attributes 'tblNameQuoted' and + // 'colNameQuoted' when necessary) + // LOGGER.warn( - // "'{}' is PostgreSQL reserved word. Do not use that word or properly escape it with backticks in mustache template", - // name); + // "'{}' is PostgreSQL reserved word. Do not use that word or properly escape it + // with backticks in mustache template", + // name); return name; } @@ -1351,12 +1434,12 @@ public void setDefaultDatabaseName(String databaseName) { * This is not related to database name which is defined by defaultDatabaseName * option. * - * @param naming identifier naming convention (original|snake_case) + * @param naming identifier naming convention (snake_case|original) */ public void setIdentifierNamingConvention(String naming) { switch (naming) { - case "original": case "snake_case": + case "original": this.identifierNamingConvention = naming; break; default: diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java index db7ca5fa3701..e97613dce294 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaCodegenTest.java @@ -30,34 +30,32 @@ public class PostgresqlSchemaCodegenTest { @Test public void testGetPostgresqlMatchedIntegerDataType() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(null, null, null), "INT"); - - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-128L, 127L, false), "TINYINT"); - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 255L, true), "TINYINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(null, null, null), "INTEGER"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-128L, 0L, false), "SMALLINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 255L, false), "SMALLINT"); Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(500L, 100L, null), "SMALLINT"); - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(500L, 100L, true), "SMALLINT"); Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(500L, 100L, false), "SMALLINT"); Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-32768L, 32767L, false), "SMALLINT"); - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 65535L, true), "SMALLINT"); - - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-8388608L, 8388607L, false), "MEDIUMINT"); - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 16777215L, true), "MEDIUMINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 65535L, false), "INTEGER"); - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-2147483648L, 2147483647L, false), "INT"); - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(Long.parseLong(String.valueOf(Integer.MIN_VALUE)), Long.parseLong(String.valueOf(Integer.MAX_VALUE)), false), "INT"); - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 4294967295L, true), "INT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-8388608L, 0L, false), "INTEGER"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 16777215L, false), "INTEGER"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(Long.parseLong(String.valueOf(Integer.MIN_VALUE)), + 0L, false), "INTEGER"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, + Long.parseLong(String.valueOf(Integer.MAX_VALUE)), false), "INTEGER"); - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-2147483649L, 2147483648L, false), "BIGINT"); - Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 4294967296L, true), "BIGINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(0L, 4294967295L, false), "BIGINT"); + Assert.assertSame(codegen.getPostgresqlMatchedIntegerDataType(-2147483649L, 0L, false), "BIGINT"); } @Test public void testGetPostgresqlMatchedStringDataType() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(6, 6), "CHAR"); - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(0, 0), "CHAR"); - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(255, 255), "CHAR"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(6, 6), "VARCHAR"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(0, 0), "VARCHAR"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(255, 255), "VARCHAR"); Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 100), "VARCHAR"); Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 255), "VARCHAR"); @@ -67,18 +65,18 @@ public void testGetPostgresqlMatchedStringDataType() { Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, null), "TEXT"); Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, null), "TEXT"); Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(255, null), "TEXT"); - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 256), "TEXT"); - - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777215, null), "MEDIUMTEXT"); - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777215, 100), "MEDIUMTEXT"); - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 16777215), "MEDIUMTEXT"); - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, 16777215), "MEDIUMTEXT"); - - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777216, null), "LONGTEXT"); - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 16777216), "LONGTEXT"); - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777216, 16777216), "LONGTEXT"); - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, 16777216), "LONGTEXT"); - Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, Integer.MAX_VALUE), "LONGTEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 256), "VARCHAR"); + + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777215, null), "TEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777215, 100), "TEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 16777215), "TEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, 16777215), "TEXT"); + + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777216, null), "TEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(null, 16777216), "TEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(16777216, 16777216), "TEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, 16777216), "TEXT"); + Assert.assertSame(codegen.getPostgresqlMatchedStringDataType(100, Integer.MAX_VALUE), "TEXT"); } @Test @@ -114,76 +112,52 @@ public void testToCodegenPostgresqlDataTypeDefault() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); HashMap defaultMap = null; ArrayList intFixture = new ArrayList(Arrays.asList( - "TINYINT", "SmallInt", "Mediumint", "INT", "bigint" - )); - for(String intType : intFixture) { + "SMALLINT", "INTEGER", "BIGINT")); + for (String intType : intFixture) { defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("150", intType); Assert.assertTrue((Boolean) defaultMap.get("isNumeric")); Assert.assertFalse((Boolean) defaultMap.get("isString")); Assert.assertFalse((Boolean) defaultMap.get("isKeyword")); Assert.assertSame(defaultMap.get("defaultValue"), "150"); } - defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("SERIAL DEFAULT VALUE", "TINYINT"); - Assert.assertFalse((Boolean) defaultMap.get("isNumeric")); - Assert.assertFalse((Boolean) defaultMap.get("isString")); - Assert.assertTrue((Boolean) defaultMap.get("isKeyword")); - Assert.assertSame(defaultMap.get("defaultValue"), "SERIAL DEFAULT VALUE"); ArrayList dateFixture = new ArrayList(Arrays.asList( - "Timestamp", "DateTime" - )); - for(String dateType : dateFixture) { + "TIMESTAMP", "DATE")); + for (String dateType : dateFixture) { defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("2018-08-12", dateType); Assert.assertFalse((Boolean) defaultMap.get("isNumeric")); Assert.assertTrue((Boolean) defaultMap.get("isString")); Assert.assertFalse((Boolean) defaultMap.get("isKeyword")); Assert.assertSame(defaultMap.get("defaultValue"), "2018-08-12"); } - defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("CURRENT_TIMESTAMP", "Timestamp"); + defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("CURRENT_TIMESTAMP", "TIMESTAMP"); Assert.assertFalse((Boolean) defaultMap.get("isNumeric")); Assert.assertFalse((Boolean) defaultMap.get("isString")); Assert.assertTrue((Boolean) defaultMap.get("isKeyword")); Assert.assertSame(defaultMap.get("defaultValue"), "CURRENT_TIMESTAMP"); - ArrayList restFixture = new ArrayList(Arrays.asList( - "VARCHAR", "CHAR", "ENUM", "UNKNOWN" - )); - for(String restType : restFixture) { - defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("sometext", restType); - Assert.assertFalse((Boolean) defaultMap.get("isNumeric")); - Assert.assertTrue((Boolean) defaultMap.get("isString")); - Assert.assertFalse((Boolean) defaultMap.get("isKeyword")); - Assert.assertSame(defaultMap.get("defaultValue"), "sometext"); - } - } - - @Test(expectedExceptions = RuntimeException.class) - public void testToCodegenPostgresqlDataTypeDefaultWithExceptionalColumnType() { - final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); - HashMap defaultMap = null; - ArrayList specialFixture = new ArrayList(Arrays.asList( - "TINYBLOB", "Blob", "MEDIUMBLOB", "LONGBLOB", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "GEOMETRY", "JSON" - )); - for(String specialType : specialFixture) { - defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("2018-08-12", specialType); - Assert.assertNull(defaultMap); - } + defaultMap = codegen.toCodegenPostgresqlDataTypeDefault("CURRENT_DATE", "DATE"); + Assert.assertFalse((Boolean) defaultMap.get("isNumeric")); + Assert.assertFalse((Boolean) defaultMap.get("isString")); + Assert.assertTrue((Boolean) defaultMap.get("isKeyword")); + Assert.assertSame(defaultMap.get("defaultValue"), "CURRENT_DATE"); } @Test public void testIsPostgresqlDataType() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); ArrayList trueFixture = new ArrayList(Arrays.asList( - "INTEGER", "integer", "Integer", "DATETIME", "datetime", "DateTime", "VARCHAR", "varchar", "VarChar", "POINT", "Point", "point", "JSON", "json", "Json" - )); + "INTEGER", "Integer", "INT", "int", "Int", "TIMESTAMP", "timestamp", "TimeStamp", "VARCHAR", "varchar", + "VarChar", "JSON", "json", "Json", "JSONB", "jsonb", "Jsonb")); ArrayList falseFixture = new ArrayList(Arrays.asList( - "unknown", "HashMap", "HASHMAP", "hashmap" - )); - for(String trueValue : trueFixture) { - Assert.assertTrue(codegen.isPostgresqlDataType(trueValue), "'" + trueValue + "' isn't PostgreSQL data type"); + "unknown", "HashMap", "HASHMAP", "hashmap")); + for (String trueValue : trueFixture) { + Assert.assertTrue(codegen.isPostgresqlDataType(trueValue), + "'" + trueValue + "' isn't PostgreSQL data type"); } - for(String falseValue : falseFixture) { - Assert.assertFalse(codegen.isPostgresqlDataType(falseValue), "'" + falseValue + "' is PostgreSQL data type"); + for (String falseValue : falseFixture) { + Assert.assertFalse(codegen.isPostgresqlDataType(falseValue), + "'" + falseValue + "' is PostgreSQL data type"); } } @@ -206,7 +180,8 @@ public void testEscapePostgresqlUnquotedIdentifier() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); Assert.assertEquals(codegen.escapePostgresqlUnquotedIdentifier("table1Z$_"), "table1Z$_"); Assert.assertEquals(codegen.escapePostgresqlUnquotedIdentifier("table1Z$_!#%~&?()*+-./"), "table1Z$_"); - Assert.assertEquals(codegen.escapePostgresqlUnquotedIdentifier("table1Z$_русскийтекст"), "table1Z$_русскийтекст"); + Assert.assertEquals(codegen.escapePostgresqlUnquotedIdentifier("table1Z$_русскийтекст"), + "table1Z$_русскийтекст"); Assert.assertEquals(codegen.escapePostgresqlQuotedIdentifier("table𐀀"), "table"); Assert.assertEquals(codegen.escapePostgresqlQuotedIdentifier("table_name!'()�"), "table_name!'()�"); Assert.assertEquals(codegen.escapePostgresqlQuotedIdentifier("table_name𐌅𐌌"), "table_name"); @@ -226,10 +201,10 @@ public void testIsReservedWord() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); Set reservedWords = codegen.reservedWords(); ArrayList trueFixture = new ArrayList(Arrays.asList( - "accessible", "asc", "between", "blob", "change", "column", "day_hour", "distinct", "enclosed", "except", "explain", "float", "for", "function", "grant", "grouping", "high_priority", "groups", "hour_minute", "insensitive", "interval", "json_table", "keys", "kill", "leave", "left", "mediumblob", "modifies", "not", "null", "numeric", "optimize", "outer", "precision", "primary", "references", "replace", "select", "sql", "then", "tinytext", "unique", "unlock", "varchar", "virtual", "when", "where", "xor", "year_month", "zerofill" + "abort", "absent", "access", "action", "admin", "after", "alter", "always", "array", "atomic", "attach", "base64", "before", "begin", "bigint", "binary", "btrim", "cache", "called", "chain", "check", "class", "close", "cobol", "column", "commit", "count", "create", "cross", "cursor", "cycle", "define", "degree", "delete", "depth", "deref", "detach", "domain", "double", "empty", "enable", "equals", "error", "escape", "event", "every", "except", "exists", "false", "family", "fetch", "filter", "final", "finish", "first", "float", "floor", "force", "format", "found", "freeze", "fusion", "global", "grant", "group", "groups", "having", "header", "ignore", "ilike", "import", "indent", "index", "inline", "inner", "inout", "input", "insert", "isnull", "label", "large", "least", "length", "level", "limit", "listen", "local", "locked", "log10", "logged", "lower", "ltrim", "match", "member", "merge", "method", "minute", "module", "month", "mumps", "names", "nchar", "nclob", "nested", "notify", "nowait", "ntile", "nullif", "nulls", "number", "object", "octets", "offset", "option", "order", "others", "outer", "output", "owned", "owner", "parser", "pascal", "period", "plans", "policy", "power", "prior", "prune", "public", "quote", "quotes", "range", "reads", "rename", "reset", "result", "return", "revoke", "right", "rollup", "rtrim", "scalar", "scale", "schema", "scope", "scroll", "search", "second", "select", "server", "setof", "share", "simple", "source", "space", "stable", "start", "state", "static", "stdin", "stdout", "stored", "strict", "string", "strip", "style", "subset", "sysid", "system", "table", "tables", "target", "token", "treat", "types", "under", "union", "unique", "unlink", "unnest", "until", "update", "upper", "usage", "using", "utf16", "utf32", "vacuum" )); ArrayList falseFixture = new ArrayList(Arrays.asList( - "after", "boolean", "charset", "cpu", "current", "delay_key_write", "end", "format", "global", "host", "install", "json", "key_block_size", "local", "max_size", "none", "offset", "partial", "quarter", "relay", "second", "status", "timestamp", "until", "variables", "without", "xml", "year" + "after_nine", "cpu", "delay_key_write", "form", "host", "install", "key_block_size", "max_size", "noo_one", "particle", "quarter", "relay", "first_do", "status", "until_now", "variables" )); for(String trueValue : trueFixture) { Assert.assertTrue(reservedWords.contains(trueValue), "'" + trueValue + "' isn't PostgreSQL reserved word"); @@ -257,20 +232,23 @@ public void testGetDefaultDatabaseName() { @Test public void testSetJsonDataType() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertSame("json", codegen.getJsonDataType()); codegen.setJsonDataType("off"); - Assert.assertTrue(codegen.getJsonDataType().equals("off")); + Assert.assertSame("off", codegen.getJsonDataType()); codegen.setJsonDataType("json"); - Assert.assertTrue(codegen.getJsonDataType().equals("json")); + Assert.assertSame("json", codegen.getJsonDataType()); codegen.setJsonDataType("jsonb"); - Assert.assertTrue(codegen.getJsonDataType().equals("jsonb")); + Assert.assertSame("jsonb", codegen.getJsonDataType()); } @Test public void testGetJsonDataType() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); - Assert.assertTrue(codegen.getJsonDataType().equals("json")); + Assert.assertSame("json", codegen.getJsonDataType()); + codegen.setJsonDataType("jsonb"); + Assert.assertSame("jsonb", codegen.getJsonDataType()); codegen.setJsonDataType("off"); - Assert.assertTrue(codegen.getJsonDataType().equals("off")); + Assert.assertSame("off", codegen.getJsonDataType()); } @Test @@ -293,21 +271,38 @@ public void testGetNamedParametersEnabled() { @Test public void testSetIdentifierNamingConvention() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); - Assert.assertSame("original", codegen.getIdentifierNamingConvention()); + Assert.assertSame("snake_case", codegen.getIdentifierNamingConvention()); codegen.setIdentifierNamingConvention("invalidValue"); - Assert.assertSame("original", codegen.getIdentifierNamingConvention()); - codegen.setIdentifierNamingConvention("snake_case"); Assert.assertSame("snake_case", codegen.getIdentifierNamingConvention()); + codegen.setIdentifierNamingConvention("original"); + Assert.assertSame("original", codegen.getIdentifierNamingConvention()); codegen.setIdentifierNamingConvention("anotherInvalid"); - Assert.assertSame("snake_case", codegen.getIdentifierNamingConvention()); + Assert.assertSame("original", codegen.getIdentifierNamingConvention()); } @Test public void testGetIdentifierNamingConvention() { final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); - Assert.assertSame("original", codegen.getIdentifierNamingConvention()); - codegen.setIdentifierNamingConvention("snake_case"); Assert.assertSame("snake_case", codegen.getIdentifierNamingConvention()); + codegen.setIdentifierNamingConvention("original"); + Assert.assertSame("original", codegen.getIdentifierNamingConvention()); + } + + @Test + public void testSetIdAutoIncEnabled() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + codegen.setIdAutoIncEnabled(true); + Assert.assertTrue(codegen.getIdAutoIncEnabled()); + codegen.setIdAutoIncEnabled(false); + Assert.assertFalse(codegen.getIdAutoIncEnabled()); + } + + @Test + public void testGetIdAutoIncEnabled() { + final PostgresqlSchemaCodegen codegen = new PostgresqlSchemaCodegen(); + Assert.assertFalse(codegen.getIdAutoIncEnabled()); + codegen.setIdAutoIncEnabled(true); + Assert.assertTrue(codegen.getIdAutoIncEnabled()); } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java index d3582a69d283..6c40cfe0ed27 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/postgresql/PostgresqlSchemaOptionsTest.java @@ -43,5 +43,6 @@ protected void verifyOptions() { verify(clientCodegen).setJsonDataType(PostgresqlSchemaOptionsProvider.JSON_DATA_TYPE_VALUE); verify(clientCodegen).setIdentifierNamingConvention(PostgresqlSchemaOptionsProvider.IDENTIFIER_NAMING_CONVENTION_VALUE); verify(clientCodegen).setNamedParametersEnabled(Boolean.valueOf(PostgresqlSchemaOptionsProvider.NAMED_PARAMETERS_ENABLED_VALUE)); + verify(clientCodegen).setIdAutoIncEnabled(Boolean.valueOf(PostgresqlSchemaOptionsProvider.ID_AUTOINC_ENABLED_VALUE)); } } diff --git a/samples/schema/petstore/postgresql/Model/ApiResponse.sql b/samples/schema/petstore/postgresql/Model/ApiResponse.sql index 67cb30e9f49e..e7192e3aab8c 100644 --- a/samples/schema/petstore/postgresql/Model/ApiResponse.sql +++ b/samples/schema/petstore/postgresql/Model/ApiResponse.sql @@ -7,22 +7,22 @@ -- --- SELECT template for table 'ApiResponse' +-- SELECT template for table 'api_response' -- -SELECT code, "type", message FROM ApiResponse WHERE 1=1; +SELECT code, "type", message FROM api_response WHERE 1=1; -- --- INSERT template for table 'ApiResponse' +-- INSERT template for table 'api_response' -- -INSERT INTO ApiResponse (code, "type", message) VALUES (?, ?, ?); +INSERT INTO api_response (code, "type", message) VALUES (?, ?, ?); -- --- UPDATE template for table 'ApiResponse' +-- UPDATE template for table 'api_response' -- -UPDATE ApiResponse SET code = ?, "type" = ?, message = ? WHERE 1=2; +UPDATE api_response SET code = ?, "type" = ?, message = ? WHERE 1=2; -- --- DELETE template for table 'ApiResponse' +-- DELETE template for table 'api_response' -- -DELETE FROM ApiResponse WHERE 1=2; +DELETE FROM api_response WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/Model/Category.sql b/samples/schema/petstore/postgresql/Model/Category.sql index 2b8187dffb4a..1675ef7da3fa 100644 --- a/samples/schema/petstore/postgresql/Model/Category.sql +++ b/samples/schema/petstore/postgresql/Model/Category.sql @@ -7,22 +7,22 @@ -- --- SELECT template for table 'Category' +-- SELECT template for table 'category' -- -SELECT id, "name" FROM Category WHERE 1=1; +SELECT "id", "name" FROM category WHERE 1=1; -- --- INSERT template for table 'Category' +-- INSERT template for table 'category' -- -INSERT INTO Category (id, "name") VALUES (?, ?); +INSERT INTO category ("id", "name") VALUES (?, ?); -- --- UPDATE template for table 'Category' +-- UPDATE template for table 'category' -- -UPDATE Category SET id = ?, "name" = ? WHERE 1=2; +UPDATE category SET "id" = ?, "name" = ? WHERE 1=2; -- --- DELETE template for table 'Category' +-- DELETE template for table 'category' -- -DELETE FROM Category WHERE 1=2; +DELETE FROM category WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/Model/Order.sql b/samples/schema/petstore/postgresql/Model/Order.sql index 78561f89e494..4bef395bf0f7 100644 --- a/samples/schema/petstore/postgresql/Model/Order.sql +++ b/samples/schema/petstore/postgresql/Model/Order.sql @@ -7,22 +7,22 @@ -- --- SELECT template for table 'Order' +-- SELECT template for table 'order' -- -SELECT id, petId, quantity, shipDate, status, complete FROM "Order" WHERE 1=1; +SELECT "id", pet_id, quantity, ship_date, status, complete FROM "order" WHERE 1=1; -- --- INSERT template for table 'Order' +-- INSERT template for table 'order' -- -INSERT INTO "Order" (id, petId, quantity, shipDate, status, complete) VALUES (?, ?, ?, ?, ?, ?); +INSERT INTO "order" ("id", pet_id, quantity, ship_date, status, complete) VALUES (?, ?, ?, ?, ?, ?); -- --- UPDATE template for table 'Order' +-- UPDATE template for table 'order' -- -UPDATE "Order" SET id = ?, petId = ?, quantity = ?, shipDate = ?, status = ?, complete = ? WHERE 1=2; +UPDATE "order" SET "id" = ?, pet_id = ?, quantity = ?, ship_date = ?, status = ?, complete = ? WHERE 1=2; -- --- DELETE template for table 'Order' +-- DELETE template for table 'order' -- -DELETE FROM "Order" WHERE 1=2; +DELETE FROM "order" WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/Model/Pet.sql b/samples/schema/petstore/postgresql/Model/Pet.sql index 389b4eaf12ad..e08b281bd7d8 100644 --- a/samples/schema/petstore/postgresql/Model/Pet.sql +++ b/samples/schema/petstore/postgresql/Model/Pet.sql @@ -7,22 +7,22 @@ -- --- SELECT template for table 'Pet' +-- SELECT template for table 'pet' -- -SELECT id, category, "name", photoUrls, tags, status FROM Pet WHERE 1=1; +SELECT "id", category, "name", photo_urls, tags, status FROM pet WHERE 1=1; -- --- INSERT template for table 'Pet' +-- INSERT template for table 'pet' -- -INSERT INTO Pet (id, category, "name", photoUrls, tags, status) VALUES (?, ?, ?, ?, ?, ?); +INSERT INTO pet ("id", category, "name", photo_urls, tags, status) VALUES (?, ?, ?, ?, ?, ?); -- --- UPDATE template for table 'Pet' +-- UPDATE template for table 'pet' -- -UPDATE Pet SET id = ?, category = ?, "name" = ?, photoUrls = ?, tags = ?, status = ? WHERE 1=2; +UPDATE pet SET "id" = ?, category = ?, "name" = ?, photo_urls = ?, tags = ?, status = ? WHERE 1=2; -- --- DELETE template for table 'Pet' +-- DELETE template for table 'pet' -- -DELETE FROM Pet WHERE 1=2; +DELETE FROM pet WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/Model/Tag.sql b/samples/schema/petstore/postgresql/Model/Tag.sql index 2622e9446279..abd78b077a77 100644 --- a/samples/schema/petstore/postgresql/Model/Tag.sql +++ b/samples/schema/petstore/postgresql/Model/Tag.sql @@ -7,22 +7,22 @@ -- --- SELECT template for table 'Tag' +-- SELECT template for table 'tag' -- -SELECT id, "name" FROM Tag WHERE 1=1; +SELECT "id", "name" FROM tag WHERE 1=1; -- --- INSERT template for table 'Tag' +-- INSERT template for table 'tag' -- -INSERT INTO Tag (id, "name") VALUES (?, ?); +INSERT INTO tag ("id", "name") VALUES (?, ?); -- --- UPDATE template for table 'Tag' +-- UPDATE template for table 'tag' -- -UPDATE Tag SET id = ?, "name" = ? WHERE 1=2; +UPDATE tag SET "id" = ?, "name" = ? WHERE 1=2; -- --- DELETE template for table 'Tag' +-- DELETE template for table 'tag' -- -DELETE FROM Tag WHERE 1=2; +DELETE FROM tag WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/Model/User.sql b/samples/schema/petstore/postgresql/Model/User.sql index cbe46e7f7552..2798d5315f38 100644 --- a/samples/schema/petstore/postgresql/Model/User.sql +++ b/samples/schema/petstore/postgresql/Model/User.sql @@ -7,22 +7,22 @@ -- --- SELECT template for table 'User' +-- SELECT template for table 'user' -- -SELECT id, username, firstName, lastName, email, "password", phone, userStatus FROM "User" WHERE 1=1; +SELECT "id", username, first_name, last_name, email, "password", phone, user_status FROM "user" WHERE 1=1; -- --- INSERT template for table 'User' +-- INSERT template for table 'user' -- -INSERT INTO "User" (id, username, firstName, lastName, email, "password", phone, userStatus) VALUES (?, ?, ?, ?, ?, ?, ?, ?); +INSERT INTO "user" ("id", username, first_name, last_name, email, "password", phone, user_status) VALUES (?, ?, ?, ?, ?, ?, ?, ?); -- --- UPDATE template for table 'User' +-- UPDATE template for table 'user' -- -UPDATE "User" SET id = ?, username = ?, firstName = ?, lastName = ?, email = ?, "password" = ?, phone = ?, userStatus = ? WHERE 1=2; +UPDATE "user" SET "id" = ?, username = ?, first_name = ?, last_name = ?, email = ?, "password" = ?, phone = ?, user_status = ? WHERE 1=2; -- --- DELETE template for table 'User' +-- DELETE template for table 'user' -- -DELETE FROM "User" WHERE 1=2; +DELETE FROM "user" WHERE 1=2; diff --git a/samples/schema/petstore/postgresql/postgresql_schema.sql b/samples/schema/petstore/postgresql/postgresql_schema.sql index 0e2e335f4cda..504ce2925c17 100644 --- a/samples/schema/petstore/postgresql/postgresql_schema.sql +++ b/samples/schema/petstore/postgresql/postgresql_schema.sql @@ -11,18 +11,18 @@ -- -- TABLES -- --- DROP TABLE IF EXISTS ApiResponse; --- DROP TABLE IF EXISTS Category; --- DROP TABLE IF EXISTS "Order"; --- DROP TABLE IF EXISTS Pet; --- DROP TABLE IF EXISTS Tag; --- DROP TABLE IF EXISTS "User"; +-- DROP TABLE IF EXISTS api_response; +-- DROP TABLE IF EXISTS category; +-- DROP TABLE IF EXISTS "order"; +-- DROP TABLE IF EXISTS pet; +-- DROP TABLE IF EXISTS tag; +-- DROP TABLE IF EXISTS "user"; -- -- TYPES -- --- DROP TYPE IF EXISTS Order_status; --- DROP TYPE IF EXISTS Pet_status; +-- DROP TYPE IF EXISTS order_status; +-- DROP TYPE IF EXISTS pet_status; -- @@ -30,87 +30,92 @@ -- -- TYPES -- -CREATE TYPE Order_status AS ENUM('placed', 'approved', 'delivered'); -CREATE TYPE Pet_status AS ENUM('available', 'pending', 'sold'); +CREATE TYPE order_status AS ENUM('placed', 'approved', 'delivered'); +CREATE TYPE pet_status AS ENUM('available', 'pending', 'sold'); -- -- TABLES -- -- --- Table 'ApiResponse' generated from model 'ApiResponse' +-- Table 'api_response' generated from model 'ApiResponse' -- Describes the result of uploading an image resource -- -CREATE TABLE IF NOT EXISTS ApiResponse ( - code INT DEFAULT NULL, - "type" VARCHAR(255) DEFAULT NULL, - message VARCHAR(255) DEFAULT NULL +CREATE TABLE IF NOT EXISTS api_response ( + code INTEGER DEFAULT NULL, + "type" TEXT DEFAULT NULL, + message TEXT DEFAULT NULL ); -COMMENT ON TABLE ApiResponse IS 'Describes the result of uploading an image resource'; +COMMENT ON TABLE api_response IS 'Describes the result of uploading an image resource. Original model name - ApiResponse.'; -- --- Table 'Category' generated from model 'Category' +-- Table 'category' generated from model 'Category' -- A category for a pet -- -CREATE TABLE IF NOT EXISTS Category ( - id BIGINT DEFAULT NULL, - "name" VARCHAR(255) DEFAULT NULL +CREATE TABLE IF NOT EXISTS category ( + "id" BIGINT DEFAULT NULL, + "name" TEXT DEFAULT NULL ); -COMMENT ON TABLE Category IS 'A category for a pet'; +COMMENT ON TABLE category IS 'A category for a pet. Original model name - Category.'; -- --- Table 'Order' generated from model 'Order' +-- Table 'order' generated from model 'Order' -- An order for a pets from the pet store -- -CREATE TABLE IF NOT EXISTS "Order" ( - id BIGINT DEFAULT NULL, - petId BIGINT DEFAULT NULL, - quantity INT DEFAULT NULL, - shipDate TIMESTAMP DEFAULT NULL, - status Order_status DEFAULT NULL, +CREATE TABLE IF NOT EXISTS "order" ( + "id" BIGINT DEFAULT NULL, + pet_id BIGINT DEFAULT NULL, + quantity INTEGER DEFAULT NULL, + ship_date TIMESTAMP DEFAULT NULL, + status order_status DEFAULT NULL, complete BOOLEAN DEFAULT 'false' ); -COMMENT ON TABLE "Order" IS 'An order for a pets from the pet store'; -COMMENT ON COLUMN "Order".status IS 'Order Status'; +COMMENT ON TABLE "order" IS 'An order for a pets from the pet store. Original model name - Order.'; +COMMENT ON COLUMN "order".pet_id IS 'Original param name - petId.'; +COMMENT ON COLUMN "order".ship_date IS 'Original param name - shipDate.'; +COMMENT ON COLUMN "order".status IS 'Order Status'; -- --- Table 'Pet' generated from model 'Pet' +-- Table 'pet' generated from model 'Pet' -- A pet for sale in the pet store -- -CREATE TABLE IF NOT EXISTS Pet ( - id BIGINT DEFAULT NULL, +CREATE TABLE IF NOT EXISTS pet ( + "id" BIGINT DEFAULT NULL, category TEXT DEFAULT NULL, - "name" VARCHAR(255) NOT NULL, - photoUrls JSON NOT NULL, + "name" TEXT NOT NULL, + photo_urls JSON NOT NULL, tags JSON DEFAULT NULL, - status Pet_status DEFAULT NULL + status pet_status DEFAULT NULL ); -COMMENT ON TABLE Pet IS 'A pet for sale in the pet store'; -COMMENT ON COLUMN Pet.status IS 'pet status in the store'; +COMMENT ON TABLE pet IS 'A pet for sale in the pet store. Original model name - Pet.'; +COMMENT ON COLUMN pet.photo_urls IS 'Original param name - photoUrls.'; +COMMENT ON COLUMN pet.status IS 'pet status in the store'; -- --- Table 'Tag' generated from model 'Tag' +-- Table 'tag' generated from model 'Tag' -- A tag for a pet -- -CREATE TABLE IF NOT EXISTS Tag ( - id BIGINT DEFAULT NULL, - "name" VARCHAR(255) DEFAULT NULL +CREATE TABLE IF NOT EXISTS tag ( + "id" BIGINT DEFAULT NULL, + "name" TEXT DEFAULT NULL ); -COMMENT ON TABLE Tag IS 'A tag for a pet'; +COMMENT ON TABLE tag IS 'A tag for a pet. Original model name - Tag.'; -- --- Table 'User' generated from model 'User' +-- Table 'user' generated from model 'User' -- A User who is purchasing from the pet store -- -CREATE TABLE IF NOT EXISTS "User" ( - id BIGINT DEFAULT NULL, - username VARCHAR(255) DEFAULT NULL, - firstName VARCHAR(255) DEFAULT NULL, - lastName VARCHAR(255) DEFAULT NULL, - email VARCHAR(255) DEFAULT NULL, - "password" VARCHAR(255) DEFAULT NULL, - phone VARCHAR(255) DEFAULT NULL, - userStatus INT DEFAULT NULL +CREATE TABLE IF NOT EXISTS "user" ( + "id" BIGINT DEFAULT NULL, + username TEXT DEFAULT NULL, + first_name TEXT DEFAULT NULL, + last_name TEXT DEFAULT NULL, + email TEXT DEFAULT NULL, + "password" TEXT DEFAULT NULL, + phone TEXT DEFAULT NULL, + user_status INTEGER DEFAULT NULL ); -COMMENT ON TABLE "User" IS 'A User who is purchasing from the pet store'; -COMMENT ON COLUMN "User".userStatus IS 'User Status'; +COMMENT ON TABLE "user" IS 'A User who is purchasing from the pet store. Original model name - User.'; +COMMENT ON COLUMN "user".first_name IS 'Original param name - firstName.'; +COMMENT ON COLUMN "user".last_name IS 'Original param name - lastName.'; +COMMENT ON COLUMN "user".user_status IS 'User Status. Original param name - userStatus.'; From 9e72956b2e5da1701dc40d56b226013c5c5ac142 Mon Sep 17 00:00:00 2001 From: Ingars Ribners Date: Thu, 5 Dec 2024 22:54:45 +0300 Subject: [PATCH 9/9] Docs updated. --- docs/generators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generators.md b/docs/generators.md index 66b0b018f1f4..2d29254e2d49 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -168,7 +168,7 @@ The following generators are available: * [graphql-schema](generators/graphql-schema.md) * [ktorm-schema (beta)](generators/ktorm-schema.md) * [mysql-schema](generators/mysql-schema.md) -* [postgresql-schema](generators/postgresql-schema.md) +* [postgresql-schema (beta)](generators/postgresql-schema.md) * [postman-collection (beta)](generators/postman-collection.md) * [protobuf-schema (beta)](generators/protobuf-schema.md) * [wsdl-schema (beta)](generators/wsdl-schema.md)