@@ -86,30 +86,20 @@ public class DynamicModelService {
8686 private static final int MAX_VARCHAR = 65_535 ;
8787 private static final int ASC_SUFFIX_LEN = 4 ;
8888 private static final int DESC_SUFFIX_LEN = 5 ;
89- private static final String IDENTIFIER_REGEX = "[A-Za-z_][A-Za-z0-9_]*" ;
90- private static final String LITERAL_REGEX = "'(?:''|[^'\\ r\\ n])*'" ;
91- private static final String COLUMN_TYPE_REGEX =
92- "(?:VARCHAR\\ ([1-9][0-9]{0,4}\\ )|INT|TINYINT\\ (1\\ )|DATE|DATETIME|TEXT|ENUM\\ ("
93- + LITERAL_REGEX
94- + "(?:, "
95- + LITERAL_REGEX
96- + ")*\\ ))" ;
97- private static final String ALTER_REGEX =
98- "^(?:ADD COLUMN|MODIFY COLUMN) "
99- + IDENTIFIER_REGEX
100- + " "
101- + COLUMN_TYPE_REGEX
102- + "(?: NOT NULL)?"
103- + "(?: DEFAULT "
104- + LITERAL_REGEX
105- + ")?"
106- + "(?: COMMENT "
107- + LITERAL_REGEX
108- + ")?"
109- + "(?: AFTER "
110- + IDENTIFIER_REGEX
111- + ")?$" ;
112- private static final String DROP_REGEX = "^DROP COLUMN " + IDENTIFIER_REGEX + "$" ;
89+
90+ private enum AlterOperationType {
91+ ADD ,
92+ MODIFY ,
93+ DROP
94+ }
95+
96+ private record AlterOperation (
97+ AlterOperationType type ,
98+ String columnName ,
99+ ParametersDto parameter ,
100+ String columnType ,
101+ String afterColumn ) {
102+ }
113103
114104 private final JdbcTemplate jdbcTemplate ;
115105 private final NamedParameterJdbcTemplate namedJdbcTemplate ;
@@ -510,7 +500,7 @@ public void modifyTableStructure(Model model) {
510500 col -> col .get ("DATA_TYPE" )));
511501
512502 // Generate ALTER TABLE statements
513- List <String > alterStatements = new ArrayList <>();
503+ List <AlterOperation > alterOperations = new ArrayList <>();
514504 // Add or modify columns based on parameters
515505 for (int i = 0 ; i < parameters .size (); i ++) {
516506 String afterColumn = null ;
@@ -525,14 +515,22 @@ public void modifyTableStructure(Model model) {
525515
526516 if (!existingColumnMap .containsKey (columnName )) {
527517 // Add new column
528- String addSql = generateColumnDefinition (param , "add" );
529- if (afterColumn != null ) {
530- addSql += " AFTER " + afterColumn ;
531- }
532- alterStatements .add (addSql );
518+ alterOperations .add (
519+ new AlterOperation (
520+ AlterOperationType .ADD ,
521+ columnName ,
522+ param ,
523+ null ,
524+ afterColumn ));
533525 } else if (!existingColumnMap .get (columnName ).equalsIgnoreCase (columnType )) {
534526 // Modify existing column
535- alterStatements .add (String .format ("MODIFY COLUMN %s %s" , columnName , columnType ));
527+ alterOperations .add (
528+ new AlterOperation (
529+ AlterOperationType .MODIFY ,
530+ columnName ,
531+ null ,
532+ columnType ,
533+ null ));
536534 }
537535 }
538536
@@ -542,29 +540,93 @@ public void modifyTableStructure(Model model) {
542540 for (String existingColumn : existingColumnMap .keySet ()) {
543541 SqlIdentifierValidator .validate (existingColumn );
544542 if (parameters .stream ().noneMatch (param -> param .getProp ().equals (existingColumn ))) {
545- alterStatements .add (String .format ("DROP COLUMN %s" , existingColumn ));
543+ alterOperations .add (
544+ new AlterOperation (
545+ AlterOperationType .DROP ,
546+ existingColumn ,
547+ null ,
548+ null ,
549+ null ));
546550 }
547551 }
548552
549553 // Execute ALTER TABLE statements
550- for (String alterStatement : alterStatements ) {
551- executeAlterTableStatement (tableName , alterStatement );
554+ for (AlterOperation operation : alterOperations ) {
555+ executeAlterTableStatement (tableName , operation );
552556 }
553557 }
554558
555- private void executeAlterTableStatement (final String tableName , final String alterStatement ) {
556- if (!tableName .matches ("^dynamic_[a-z0-9_]+$" )) {
557- throw new IllegalArgumentException ("Invalid dynamic table name" );
558- }
559- if (alterStatement == null
560- || !alterStatement .matches (ALTER_REGEX )
561- && !alterStatement .matches (DROP_REGEX )) {
562- throw new IllegalArgumentException ("Invalid ALTER TABLE statement" );
559+ private void executeAlterTableStatement (
560+ final String tableName , final AlterOperation operation ) {
561+ String safeTable = requireAlterIdentifier (tableName , "tableName" );
562+ String sql ;
563+ switch (operation .type ()) {
564+ case ADD :
565+ sql = buildAddColumnSql (operation , safeTable );
566+ break ;
567+ case MODIFY :
568+ sql = buildModifyColumnSql (operation , safeTable );
569+ break ;
570+ case DROP :
571+ sql =
572+ "ALTER TABLE "
573+ + safeTable
574+ + " DROP COLUMN "
575+ + requireAlterIdentifier (operation .columnName (), "columnName" );
576+ break ;
577+ default :
578+ throw new IllegalArgumentException ("Unsupported ALTER TABLE operation" );
563579 }
564- final String sql = String .format ("ALTER TABLE %s %s" , tableName , alterStatement );
565580 jdbcTemplate .execute (sql );
566581 }
567582
583+ private String buildAddColumnSql (
584+ final AlterOperation operation , final String tableName ) {
585+ requireAlterIdentifier (operation .columnName (), "columnName" );
586+ final String columnDefinition = generateColumnDefinition (operation .parameter (), "add" );
587+ final String afterColumn = operation .afterColumn ();
588+ return afterColumn == null
589+ ? "ALTER TABLE " + tableName + " " + columnDefinition
590+ : "ALTER TABLE "
591+ + tableName
592+ + " "
593+ + columnDefinition
594+ + " AFTER "
595+ + requireAlterIdentifier (afterColumn , "afterColumn" );
596+ }
597+
598+ private String buildModifyColumnSql (
599+ final AlterOperation operation , final String tableName ) {
600+ return "ALTER TABLE "
601+ + tableName
602+ + " MODIFY COLUMN "
603+ + requireAlterIdentifier (operation .columnName (), "columnName" )
604+ + " "
605+ + requireColumnType (operation .columnType ());
606+ }
607+
608+ private String requireAlterIdentifier (final String value , final String name ) {
609+ if (value == null || !value .matches ("^[A-Za-z_][A-Za-z0-9_]*$" )) {
610+ throw new IllegalArgumentException (name + " must be a valid SQL identifier" );
611+ }
612+ return value ;
613+ }
614+
615+ private String requireColumnType (final String value ) {
616+ if (value == null
617+ || !(value .equals ("INT" )
618+ || value .equals ("TINYINT" )
619+ || value .equals ("DATE" )
620+ || value .equals ("TIMESTAMP" )
621+ || value .equals ("VARCHAR" )
622+ || value .equals ("Enum" )
623+ || value .equals ("TEXT" )
624+ || value .matches ("^VARCHAR\\ ([1-9][0-9]{0,4}\\ )$" ))) {
625+ throw new IllegalArgumentException ("Invalid SQL column type" );
626+ }
627+ return value ;
628+ }
629+
568630 private void addCommonFields (List <ParametersDto > parameters ) {
569631 ParametersDto id = new ParametersDto ();
570632 id .setProp ("id" );
0 commit comments