<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="screenManagement">

  <!-- ─── Screen definitions ──────────────────────────────────────── -->

  <select id="checkScreenCodeExists" parameterType="map" resultType="int">
    SELECT COUNT(*)
    FROM SCREEN_DEFINITIONS
    WHERE SCREEN_CODE = #{screen_code}
      AND IS_ACTIVE != 'D'
  </select>

  <select id="insertScreen" parameterType="map" resultType="map">
    INSERT INTO SCREEN_DEFINITIONS (
        SCREEN_NAME
        , SCREEN_CODE
        , TABLE_NAME
        , COMPANY_CODE
        , DESCRIPTION
        , CREATED_BY
        , DB_SOURCE_TYPE
        , DB_CONNECTION_ID
        , DATA_SOURCE_TYPE
        , REST_API_CONNECTION_ID
        , REST_API_ENDPOINT
        , REST_API_JSON_PATH
    ) VALUES (
        #{screen_name}
        , #{screen_code}
        , #{table_name}
        , #{company_code}
        , #{description}
        , #{created_by}
        , #{db_source_type}
        , #{db_connection_id}
        , #{data_source_type}
        , #{rest_api_connection_id}
        , #{rest_api_endpoint}
        , #{rest_api_json_path}
    )
    RETURNING
        SCREEN_ID
        , SCREEN_NAME
        , SCREEN_CODE
        , TABLE_NAME
        , COMPANY_CODE
        , DESCRIPTION
        , IS_ACTIVE
        , CREATED_DATE
        , CREATED_BY
        , UPDATED_DATE
        , UPDATED_BY
        , DB_SOURCE_TYPE
        , DB_CONNECTION_ID
        , DATA_SOURCE_TYPE
        , REST_API_CONNECTION_ID
        , REST_API_ENDPOINT
        , REST_API_JSON_PATH
  </select>

  <select id="selectScreenList" parameterType="map" resultType="map">
    SELECT
        SD.*
    FROM SCREEN_DEFINITIONS SD
    WHERE SD.IS_ACTIVE != 'D'
    <if test="company_code != null and company_code != &quot;*&quot;">
      AND SD.COMPANY_CODE = #{company_code}
    </if>
    <if test="search_term != null and search_term != ''">
      AND (SD.SCREEN_NAME ILIKE #{search_like}
        OR SD.SCREEN_CODE ILIKE #{search_like}
        OR SD.TABLE_NAME ILIKE #{search_like})
    </if>
    <if test="exclude_pop != null and exclude_pop == true">
      AND NOT EXISTS (
          SELECT 1
          FROM SCREEN_LAYOUTS_POP SLP
          WHERE SLP.SCREEN_ID = SD.SCREEN_ID
      )
    </if>
    ORDER BY SD.CREATED_DATE DESC
    LIMIT #{size} OFFSET #{offset}
  </select>

  <select id="countScreenList" parameterType="map" resultType="int">
    SELECT COUNT(*)
    FROM SCREEN_DEFINITIONS SD
    WHERE SD.IS_ACTIVE != 'D'
    <if test="company_code != null and company_code != &quot;*&quot;">
      AND SD.COMPANY_CODE = #{company_code}
    </if>
    <if test="search_term != null and search_term != ''">
      AND (SD.SCREEN_NAME ILIKE #{search_like}
        OR SD.SCREEN_CODE ILIKE #{search_like}
        OR SD.TABLE_NAME ILIKE #{search_like})
    </if>
    <if test="exclude_pop != null and exclude_pop == true">
      AND NOT EXISTS (
          SELECT 1
          FROM SCREEN_LAYOUTS_POP SLP
          WHERE SLP.SCREEN_ID = SD.SCREEN_ID
      )
    </if>
  </select>

  <select id="selectScreenById" parameterType="map" resultType="map">
    SELECT
        SD.SCREEN_ID
        , SD.SCREEN_NAME
        , SD.SCREEN_CODE
        , SD.TABLE_NAME
        , SD.COMPANY_CODE
        , SD.DESCRIPTION
        , SD.IS_ACTIVE
        , SD.CREATED_DATE
        , SD.CREATED_BY
        , SD.UPDATED_DATE
        , COALESCE(SD.UPDATED_BY, '') AS UPDATED_BY
        , SD.DB_SOURCE_TYPE
        , SD.DB_CONNECTION_ID
        , SD.DATA_SOURCE_TYPE
        , SD.REST_API_CONNECTION_ID
        , SD.REST_API_ENDPOINT
        , COALESCE(SD.REST_API_JSON_PATH, 'data') AS REST_API_JSON_PATH
        , SD.DELETED_DATE
        , SD.DELETED_BY
        , SD.DELETE_REASON
        , COALESCE(TL.TABLE_LABEL, SD.TABLE_NAME) AS TABLE_LABEL
    FROM SCREEN_DEFINITIONS SD
    LEFT JOIN TABLE_LABELS TL ON SD.TABLE_NAME = TL.TABLE_NAME
    WHERE SD.SCREEN_ID = #{screen_id}
    LIMIT 1
  </select>

  <update id="updateScreen" parameterType="map">
    UPDATE SCREEN_DEFINITIONS
    SET
        SCREEN_NAME = #{screen_name}
        , DESCRIPTION = #{description}
        , IS_ACTIVE = #{is_active}
        , UPDATED_BY = #{updated_by}
        , UPDATED_DATE = NOW()
    WHERE SCREEN_ID = #{screen_id}
  </update>

  <update id="updateScreenInfo" parameterType="map">
    UPDATE SCREEN_DEFINITIONS
    SET
        SCREEN_NAME = #{screen_name}
        , TABLE_NAME = #{table_name}
        , DESCRIPTION = #{description}
        , IS_ACTIVE = #{is_active}
        , UPDATED_DATE = NOW()
        , DATA_SOURCE_TYPE = #{data_source_type}
        , DB_SOURCE_TYPE = #{db_source_type}
        , DB_CONNECTION_ID = #{db_connection_id}
        , REST_API_CONNECTION_ID = #{rest_api_connection_id}
        , REST_API_ENDPOINT = #{rest_api_endpoint}
        , REST_API_JSON_PATH = #{rest_api_json_path}
    WHERE SCREEN_ID = #{screen_id}
  </update>

  <update id="updateScreenTableName" parameterType="map">
    UPDATE SCREEN_DEFINITIONS
    SET
        TABLE_NAME = #{table_name}
        , UPDATED_DATE = NOW()
    WHERE SCREEN_ID = #{screen_id}
  </update>

  <update id="softDeleteScreen" parameterType="map">
    UPDATE SCREEN_DEFINITIONS
    SET
        IS_ACTIVE = 'D'
        , DELETED_DATE = NOW()
        , DELETED_BY = #{deleted_by}
        , DELETE_REASON = #{delete_reason}
        , UPDATED_DATE = NOW()
        , UPDATED_BY = #{deleted_by}
    WHERE SCREEN_ID = #{screen_id}
  </update>

  <update id="deactivateMenuAssignmentsByScreen" parameterType="map">
    UPDATE SCREEN_MENU_ASSIGNMENTS
    SET
        IS_ACTIVE = 'N'
    WHERE SCREEN_ID = #{screen_id}
      AND IS_ACTIVE = 'Y'
  </update>

  <delete id="deleteScreenGroupLinks" parameterType="map">
    DELETE FROM SCREEN_GROUP_SCREENS
    WHERE SCREEN_ID = #{screen_id}
  </delete>

  <update id="restoreScreen" parameterType="map">
    UPDATE SCREEN_DEFINITIONS
    SET
        IS_ACTIVE = 'Y'
        , DELETED_DATE = NULL
        , DELETED_BY = NULL
        , DELETE_REASON = NULL
        , UPDATED_DATE = NOW()
        , UPDATED_BY = #{restored_by}
    WHERE SCREEN_ID = #{screen_id}
  </update>

  <update id="reactivateMenuAssignmentsByScreen" parameterType="map">
    UPDATE SCREEN_MENU_ASSIGNMENTS
    SET
        IS_ACTIVE = 'Y'
    WHERE SCREEN_ID = #{screen_id}
      AND IS_ACTIVE = 'N'
  </update>

  <delete id="permanentDeleteLayoutV1" parameterType="map">
    DELETE FROM SCREEN_LAYOUTS
    WHERE SCREEN_ID = #{screen_id}
  </delete>

  <delete id="permanentDeleteMenuAssignmentsByScreen" parameterType="map">
    DELETE FROM SCREEN_MENU_ASSIGNMENTS
    WHERE SCREEN_ID = #{screen_id}
  </delete>

  <delete id="permanentDeleteScreen" parameterType="map">
    DELETE FROM SCREEN_DEFINITIONS
    WHERE SCREEN_ID = #{screen_id}
  </delete>

  <select id="selectDeletedScreenList" parameterType="map" resultType="map">
    SELECT
        SD.SCREEN_ID
        , SD.SCREEN_NAME
        , SD.SCREEN_CODE
        , COALESCE(SD.TABLE_NAME, '') AS TABLE_NAME
        , SD.COMPANY_CODE
        , COALESCE(SD.DESCRIPTION, '') AS DESCRIPTION
        , SD.IS_ACTIVE
        , SD.CREATED_DATE
        , COALESCE(SD.CREATED_BY, '') AS CREATED_BY
        , SD.UPDATED_DATE
        , COALESCE(SD.UPDATED_BY, '') AS UPDATED_BY
        , COALESCE(SD.DB_SOURCE_TYPE, '') AS DB_SOURCE_TYPE
        , COALESCE(SD.DATA_SOURCE_TYPE, '') AS DATA_SOURCE_TYPE
        , COALESCE(SD.REST_API_JSON_PATH, '') AS REST_API_JSON_PATH
        , SD.DELETED_DATE
        , COALESCE(SD.DELETED_BY, '') AS DELETED_BY
        , COALESCE(SD.DELETE_REASON, '') AS DELETE_REASON
        , COALESCE(TL.TABLE_LABEL, SD.TABLE_NAME, '') AS TABLE_LABEL
    FROM SCREEN_DEFINITIONS SD
    LEFT JOIN TABLE_LABELS TL ON SD.TABLE_NAME = TL.TABLE_NAME
    WHERE SD.IS_ACTIVE = 'D'
    <if test="company_code != null and company_code != &quot;*&quot;">
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
    </if>
    ORDER BY DELETED_DATE DESC NULLS LAST
    LIMIT #{size} OFFSET #{offset}
  </select>

  <select id="countDeletedScreenList" parameterType="map" resultType="int">
    SELECT COUNT(*)
    FROM SCREEN_DEFINITIONS
    WHERE IS_ACTIVE = 'D'
    <if test="company_code != null and company_code != &quot;*&quot;">
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
    </if>
  </select>

  <select id="selectTableLabelsByNames" parameterType="map" resultType="map">
    SELECT
        TABLE_NAME
        , TABLE_LABEL
    FROM TABLE_LABELS
    WHERE TABLE_NAME IN
    <foreach collection="tableNames" item="name" open="(" separator="," close=")">
      #{name}
    </foreach>
  </select>

  <select id="checkDuplicateScreenName" parameterType="map" resultType="int">
    SELECT COUNT(*)
    FROM SCREEN_DEFINITIONS
    WHERE (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
      AND SCREEN_NAME = #{screen_name}
      AND DELETED_DATE IS NULL
  </select>

  <select id="checkScreenCodeForRestore" parameterType="map" resultType="int">
    SELECT COUNT(*)
    FROM SCREEN_DEFINITIONS
    WHERE SCREEN_CODE = #{screen_code}
      AND IS_ACTIVE != 'D'
      AND SCREEN_ID != #{screen_id}
  </select>

  <!-- ─── Screen code generation ──────────────────────────────────── -->

  <select id="pgAdvisoryXactLock" parameterType="map" resultType="map">
    SELECT PG_ADVISORY_XACT_LOCK(#{lock_id})
  </select>

  <select id="selectScreenCodesByPattern" parameterType="map" resultType="map">
    SELECT
        SCREEN_CODE
    FROM SCREEN_DEFINITIONS
    WHERE SCREEN_CODE LIKE #{pattern}
    ORDER BY SCREEN_CODE DESC
  </select>

  <!-- ─── Layout V1 ───────────────────────────────────────────────── -->

  <delete id="deleteAllLayoutV1" parameterType="map">
    DELETE FROM SCREEN_LAYOUTS
    WHERE SCREEN_ID = #{screen_id}
  </delete>

  <insert id="insertLayoutV1Component" parameterType="map">
    INSERT INTO SCREEN_LAYOUTS (
        SCREEN_ID
        , COMPONENT_TYPE
        , COMPONENT_ID
        , PARENT_ID
        , POSITION_X
        , POSITION_Y
        , WIDTH
        , HEIGHT
        , PROPERTIES
        , DISPLAY_ORDER
    ) VALUES (
        #{screen_id}
        , #{component_type}
        , #{component_id}
        , #{parent_id}
        , #{position_x}
        , #{position_y}
        , #{width}
        , #{height}
        , #{properties}::jsonb
        , #{display_order}
    )
  </insert>

  <select id="selectLayoutV1ByScreen" parameterType="map" resultType="map">
    SELECT
        *
    FROM SCREEN_LAYOUTS
    WHERE SCREEN_ID = #{screen_id}
    ORDER BY DISPLAY_ORDER ASC NULLS LAST, LAYOUT_ID ASC
  </select>

  <select id="selectLayoutV1FromV1Table" parameterType="map" resultType="map">
    SELECT
        *
    FROM SCREEN_LAYOUTS_V1
    WHERE SCREEN_ID = #{screen_id}
      AND (COMPANY_CODE = #{company_code} OR #{company_code} = '*')
    ORDER BY DISPLAY_ORDER ASC NULLS LAST, LAYOUT_ID ASC
  </select>

  <!-- ─── Layout V2 ───────────────────────────────────────────────── -->

  <select id="selectLayoutV2" parameterType="map" resultType="map">
    SELECT
        LAYOUT_DATA
        , LAYER_NAME
        , CONDITION_CONFIG
        , COMPANY_CODE
    FROM SCREEN_LAYOUTS_V2
    WHERE SCREEN_ID = #{screen_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
      <if test="layer_id != null">AND LAYER_ID = #{layer_id}</if>
    ORDER BY LAYER_ID ASC
    LIMIT 1
  </select>

  <select id="selectLayoutV2Exact" parameterType="map" resultType="map">
    SELECT
        LAYOUT_DATA
        , LAYER_NAME
        , CONDITION_CONFIG
        , COMPANY_CODE
    FROM SCREEN_LAYOUTS_V2
    WHERE SCREEN_ID = #{screen_id}
      AND COMPANY_CODE = #{company_code}
      <if test="layer_id != null">AND LAYER_ID = #{layer_id}</if>
    ORDER BY LAYER_ID ASC
    LIMIT 1
  </select>

  <insert id="upsertLayoutV2" parameterType="map">
    INSERT INTO SCREEN_LAYOUTS_V2 (
        SCREEN_ID
        , COMPANY_CODE
        , LAYER_ID
        , LAYER_NAME
        , LAYOUT_DATA
        , CREATED_DATE
        , UPDATED_DATE
    ) VALUES (
        #{screen_id}
        , #{company_code}
        , #{layer_id}
        , #{layer_name}
        , #{layout_data}::jsonb
        , NOW()
        , NOW()
    )
    ON CONFLICT (SCREEN_ID, COMPANY_CODE, LAYER_ID) DO UPDATE SET
        LAYOUT_DATA = EXCLUDED.LAYOUT_DATA
        , LAYER_NAME = EXCLUDED.LAYER_NAME
        , UPDATED_DATE = NOW()
  </insert>

  <insert id="upsertLayoutV2WithCondition" parameterType="map">
    INSERT INTO SCREEN_LAYOUTS_V2 (
        SCREEN_ID
        , COMPANY_CODE
        , LAYER_ID
        , LAYER_NAME
        , CONDITION_CONFIG
        , LAYOUT_DATA
        , CREATED_DATE
        , UPDATED_DATE
    ) VALUES (
        #{screen_id}
        , #{company_code}
        , #{layer_id}
        , #{layer_name}
        , #{condition_config}::jsonb
        , #{layout_data}::jsonb
        , NOW()
        , NOW()
    )
    ON CONFLICT (SCREEN_ID, COMPANY_CODE, LAYER_ID) DO UPDATE SET
        LAYOUT_DATA = EXCLUDED.LAYOUT_DATA
        , LAYER_NAME = EXCLUDED.LAYER_NAME
        , CONDITION_CONFIG = EXCLUDED.CONDITION_CONFIG
        , UPDATED_DATE = NOW()
  </insert>

  <update id="updateScreenMainTable" parameterType="map">
    UPDATE SCREEN_DEFINITIONS
    SET
        TABLE_NAME = #{table_name}
        , UPDATED_DATE = NOW()
    WHERE SCREEN_ID = #{screen_id}
  </update>

  <!-- ─── Layer management ────────────────────────────────────────── -->

  <select id="selectLayersByScreen" parameterType="map" resultType="map">
    SELECT
        LAYER_ID
        , LAYER_NAME
        , COALESCE(CONDITION_CONFIG, '{}') AS CONDITION_CONFIG
        , JSONB_ARRAY_LENGTH(COALESCE(LAYOUT_DATA->'components', '[]'::jsonb)) AS COMPONENT_COUNT
        , UPDATED_DATE AS UPDATED_AT
    FROM SCREEN_LAYOUTS_V2
    WHERE SCREEN_ID = #{screen_id}
      <if test='company_code != null and company_code != "*"'>
        AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
      </if>
    ORDER BY LAYER_ID
  </select>

  <insert id="upsertDefaultLayer" parameterType="map">
    INSERT INTO SCREEN_LAYOUTS_V2 (
        SCREEN_ID
        , COMPANY_CODE
        , LAYER_ID
        , LAYER_NAME
        , LAYOUT_DATA
        , CREATED_DATE
        , UPDATED_DATE
    ) VALUES (
        #{screen_id}
        , #{company_code}
        , 1
        , '기본 레이어'
        , #{layout_data}::jsonb
        , NOW()
        , NOW()
    )
    ON CONFLICT (SCREEN_ID, COMPANY_CODE, LAYER_ID) DO NOTHING
  </insert>

  <select id="selectLayerLayout" parameterType="map" resultType="map">
    SELECT
        LAYOUT_DATA
        , LAYER_NAME
        , CONDITION_CONFIG
    FROM SCREEN_LAYOUTS_V2
    WHERE SCREEN_ID = #{screen_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
      AND LAYER_ID = #{layer_id}
    LIMIT 1
  </select>

  <delete id="deleteLayerById" parameterType="map">
    DELETE FROM SCREEN_LAYOUTS_V2
    WHERE SCREEN_ID = #{screen_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
      AND LAYER_ID = #{layer_id}
  </delete>

  <update id="updateLayerCondition" parameterType="map">
    UPDATE SCREEN_LAYOUTS_V2
    <set>
      CONDITION_CONFIG = #{condition_config}::jsonb,
      UPDATED_DATE = NOW()
      <if test="layer_name != null">, LAYER_NAME = #{layer_name}</if>
    </set>
    WHERE SCREEN_ID = #{screen_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
      AND LAYER_ID = #{layer_id}
  </update>

  <select id="selectMaxLayerId" parameterType="map" resultType="map">
    SELECT
        COALESCE(MAX(LAYER_ID), 1) AS MAX_ID
    FROM SCREEN_LAYOUTS_V2
    WHERE SCREEN_ID = #{screen_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
  </select>

  <insert id="insertLayerForZone" parameterType="map">
    INSERT INTO SCREEN_LAYOUTS_V2 (
        SCREEN_ID
        , COMPANY_CODE
        , LAYOUT_DATA
        , LAYER_ID
        , LAYER_NAME
        , CONDITION_CONFIG
    ) VALUES (
        #{screen_id}
        , #{company_code}
        , #{layout_data}::jsonb
        , #{layer_id}
        , #{layer_name}
        , #{condition_config}::jsonb
    )
    ON CONFLICT (SCREEN_ID, COMPANY_CODE, LAYER_ID) DO UPDATE SET
        LAYOUT_DATA = EXCLUDED.LAYOUT_DATA
        , LAYER_NAME = EXCLUDED.LAYER_NAME
        , CONDITION_CONFIG = EXCLUDED.CONDITION_CONFIG
        , UPDATED_DATE = NOW()
  </insert>

  <!-- ─── Zone management ─────────────────────────────────────────── -->

  <select id="selectZonesByScreen" parameterType="map" resultType="map">
    SELECT
        *
    FROM SCREEN_CONDITIONAL_ZONES
    WHERE SCREEN_ID = #{screen_id}
      <if test="company_code != null and company_code != &quot;*&quot;">
        AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
      </if>
    ORDER BY ZONE_ID
  </select>

  <insert id="insertZone" parameterType="map" useGeneratedKeys="false">
    INSERT INTO SCREEN_CONDITIONAL_ZONES (
        SCREEN_ID
        , COMPANY_CODE
        , ZONE_NAME
        , X
        , Y
        , WIDTH
        , HEIGHT
        , TRIGGER_COMPONENT_ID
        , TRIGGER_OPERATOR
    ) VALUES (
        #{screen_id}
        , #{company_code}
        , #{zone_name}
        , #{x}
        , #{y}
        , #{width}
        , #{height}
        , #{trigger_component_id}
        , #{trigger_operator}
    )
    RETURNING *
  </insert>

  <update id="updateZone" parameterType="map">
    UPDATE SCREEN_CONDITIONAL_ZONES
    <set>
      UPDATED_DATE = NOW()
      <if test="zone_name != null">, ZONE_NAME = #{zone_name}</if>
      <if test="x != null">, X = #{x}</if>
      <if test="y != null">, Y = #{y}</if>
      <if test="width != null">, WIDTH = #{width}</if>
      <if test="height != null">, HEIGHT = #{height}</if>
      <if test="trigger_component_id != null">, TRIGGER_COMPONENT_ID = #{trigger_component_id}</if>
      <if test="trigger_operator != null">, TRIGGER_OPERATOR = #{trigger_operator}</if>
    </set>
    WHERE ZONE_ID = #{zone_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
  </update>

  <delete id="deleteZoneById" parameterType="map">
    DELETE FROM SCREEN_CONDITIONAL_ZONES
    WHERE ZONE_ID = #{zone_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
  </delete>

  <update id="clearZoneConditionConfigs" parameterType="map">
    UPDATE SCREEN_LAYOUTS_V2
    SET
        CONDITION_CONFIG = NULL
        , UPDATED_DATE = NOW()
    WHERE (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
      AND CONDITION_CONFIG->>'zone_id' = #{zone_id_str}
  </update>

  <select id="selectZoneById" parameterType="map" resultType="map">
    SELECT
        *
    FROM SCREEN_CONDITIONAL_ZONES
    WHERE ZONE_ID = #{zone_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
    LIMIT 1
  </select>

  <!-- ─── POP Layout ───────────────────────────────────────────────── -->

  <select id="selectLayoutPop" parameterType="map" resultType="map">
    SELECT
        LAYOUT_DATA
    FROM SCREEN_LAYOUTS_POP
    WHERE SCREEN_ID = #{screen_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
    LIMIT 1
  </select>

  <insert id="upsertLayoutPop" parameterType="map">
    INSERT INTO SCREEN_LAYOUTS_POP (
        SCREEN_ID
        , COMPANY_CODE
        , LAYOUT_DATA
        , CREATED_DATE
        , UPDATED_DATE
        , CREATED_BY
        , UPDATED_BY
    ) VALUES (
        #{screen_id}
        , #{company_code}
        , #{layout_data}::jsonb
        , NOW()
        , NOW()
        , #{user_id}
        , #{user_id}
    )
    ON CONFLICT (SCREEN_ID, COMPANY_CODE) DO UPDATE SET
        LAYOUT_DATA = EXCLUDED.LAYOUT_DATA
        , UPDATED_DATE = NOW()
        , UPDATED_BY = EXCLUDED.UPDATED_BY
  </insert>

  <delete id="deleteLayoutPopByScreen" parameterType="map">
    DELETE FROM SCREEN_LAYOUTS_POP
    WHERE SCREEN_ID = #{screen_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
  </delete>

  <select id="selectScreenIdsWithPopLayout" parameterType="map" resultType="map">
    SELECT DISTINCT
        SCREEN_ID
    FROM SCREEN_LAYOUTS_POP
    <if test="company_code != null and company_code != &quot;*&quot;">
      WHERE (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
    </if>
  </select>

  <!-- ─── Menu assignment ──────────────────────────────────────────── -->

  <select id="checkMenuAssignmentExists" parameterType="map" resultType="map">
    SELECT
        ASSIGNMENT_ID
    FROM SCREEN_MENU_ASSIGNMENTS
    WHERE SCREEN_ID = #{screen_id}
      AND MENU_OBJID = #{menu_objid}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
    LIMIT 1
  </select>

  <insert id="insertMenuAssignment" parameterType="map">
    INSERT INTO SCREEN_MENU_ASSIGNMENTS (
        SCREEN_ID
        , MENU_OBJID
        , COMPANY_CODE
        , DISPLAY_ORDER
        , CREATED_BY
    ) VALUES (
        #{screen_id}
        , #{menu_objid}
        , #{company_code}
        , #{display_order}
        , #{created_by}
    )
  </insert>

  <select id="selectScreenCode" parameterType="map" resultType="map">
    SELECT
        SCREEN_CODE
    FROM SCREEN_DEFINITIONS
    WHERE SCREEN_ID = #{screen_id}
    LIMIT 1
  </select>

  <select id="selectMenuType" parameterType="map" resultType="map">
    SELECT
        MENU_TYPE
    FROM MENU_INFO
    WHERE OBJID = #{menu_objid}
    LIMIT 1
  </select>

  <update id="updateMenuInfoAssign" parameterType="map">
    UPDATE MENU_INFO
    SET
        MENU_URL = #{menu_url}
        , SCREEN_CODE = #{screen_code}
    WHERE OBJID = #{menu_objid}
  </update>

  <delete id="deleteMenuAssignment" parameterType="map">
    DELETE FROM SCREEN_MENU_ASSIGNMENTS
    WHERE SCREEN_ID = #{screen_id}
      AND MENU_OBJID = #{menu_objid}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
  </delete>

  <update id="updateMenuInfoUnassign" parameterType="map">
    UPDATE MENU_INFO
    SET
        MENU_URL = NULL
        , SCREEN_CODE = NULL
    WHERE OBJID = #{menu_objid}
  </update>

  <select id="selectScreensByMenu" parameterType="map" resultType="map">
    SELECT
        SD.*
    FROM SCREEN_MENU_ASSIGNMENTS SMA
    INNER JOIN SCREEN_DEFINITIONS SD ON SMA.SCREEN_ID = SD.SCREEN_ID
    WHERE SMA.MENU_OBJID = #{menu_objid}
      AND (SMA.COMPANY_CODE = #{company_code} OR SMA.COMPANY_CODE = '*')
      AND SMA.IS_ACTIVE = 'Y'
    ORDER BY CASE WHEN SMA.COMPANY_CODE = #{company_code} THEN 0 ELSE 1 END
        , SMA.DISPLAY_ORDER ASC
  </select>

  <select id="selectMenuByScreen" parameterType="map" resultType="map">
    SELECT
        SMA.MENU_OBJID
        , MI.MENU_NAME_KOR
    FROM SCREEN_MENU_ASSIGNMENTS SMA
    LEFT JOIN MENU_INFO MI ON SMA.MENU_OBJID = MI.OBJID
    WHERE SMA.SCREEN_ID = #{screen_id}
      AND (SMA.COMPANY_CODE = #{company_code} OR SMA.COMPANY_CODE = '*')
      AND SMA.IS_ACTIVE = 'Y'
    ORDER BY SMA.CREATED_DATE ASC
    LIMIT 1
  </select>

  <update id="cleanupDeletedMenuAssignments" parameterType="map">
    UPDATE SCREEN_MENU_ASSIGNMENTS
    SET
        IS_ACTIVE = 'N'
    WHERE SCREEN_ID IN (
        SELECT SCREEN_ID
        FROM SCREEN_DEFINITIONS
        WHERE IS_ACTIVE = 'D'
    )
    AND IS_ACTIVE = 'Y'
  </update>

  <!-- ─── Dependencies / Linked modals ────────────────────────────── -->

  <select id="selectScreensWithLayouts" parameterType="map" resultType="map">
    SELECT
        SD.SCREEN_ID
        , SD.SCREEN_NAME
        , SD.SCREEN_CODE
        , SD.COMPANY_CODE
        , SL.LAYOUT_ID
        , SL.COMPONENT_ID
        , SL.COMPONENT_TYPE
        , SL.PROPERTIES
    FROM SCREEN_DEFINITIONS SD
    LEFT JOIN SCREEN_LAYOUTS SL ON SD.SCREEN_ID = SL.SCREEN_ID
        AND SL.COMPONENT_TYPE = 'component'
    WHERE SD.IS_ACTIVE != 'D'
      <if test="company_code != null and company_code != &quot;*&quot;">
        AND SD.COMPANY_CODE IN (#{company_code}, '*')
      </if>
  </select>

  <select id="selectMenuAssignmentDeps" parameterType="map" resultType="map">
    SELECT
        SMA.ASSIGNMENT_ID
        , SMA.MENU_OBJID
        , MI.MENU_NAME_KOR
    FROM SCREEN_MENU_ASSIGNMENTS SMA
    LEFT JOIN MENU_INFO MI ON SMA.MENU_OBJID = MI.OBJID
    WHERE SMA.SCREEN_ID = #{screen_id}
      AND SMA.IS_ACTIVE = 'Y'
  </select>

  <select id="selectLayoutsForLinkedModal" parameterType="map" resultType="map">
    SELECT
        LAYOUT_ID
        , PROPERTIES
    FROM SCREEN_LAYOUTS
    WHERE SCREEN_ID = #{screen_id}
      AND COMPONENT_TYPE = 'component'
      AND PROPERTIES IS NOT NULL
  </select>

  <select id="selectScreensByIds" parameterType="map" resultType="map">
    SELECT
        SCREEN_ID
        , SCREEN_NAME
        , SCREEN_CODE
    FROM SCREEN_DEFINITIONS
    WHERE SCREEN_ID IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
      #{id}
    </foreach>
    AND IS_ACTIVE != 'D'
    ORDER BY SCREEN_NAME
  </select>

  <!-- ─── Copy screen ──────────────────────────────────────────────── -->

  <select id="selectScreenForCopy" parameterType="map" resultType="map">
    SELECT
        *
    FROM SCREEN_DEFINITIONS
    WHERE SCREEN_ID = #{screen_id}
    LIMIT 1
  </select>

  <insert id="insertScreenCopy" parameterType="map" useGeneratedKeys="false">
    INSERT INTO SCREEN_DEFINITIONS (
        SCREEN_NAME
        , SCREEN_CODE
        , TABLE_NAME
        , COMPANY_CODE
        , DESCRIPTION
        , CREATED_BY
        , DB_SOURCE_TYPE
        , DB_CONNECTION_ID
        , DATA_SOURCE_TYPE
        , REST_API_CONNECTION_ID
        , REST_API_ENDPOINT
        , REST_API_JSON_PATH
    ) VALUES (
        #{screen_name}
        , #{screen_code}
        , #{table_name}
        , #{company_code}
        , #{description}
        , #{created_by}
        , #{db_source_type}
        , #{db_connection_id}
        , #{data_source_type}
        , #{rest_api_connection_id}
        , #{rest_api_endpoint}
        , #{rest_api_json_path}
    )
    RETURNING
        SCREEN_ID
        , SCREEN_NAME
        , SCREEN_CODE
        , TABLE_NAME
        , COMPANY_CODE
        , DESCRIPTION
        , IS_ACTIVE
        , CREATED_DATE
        , CREATED_BY
        , DB_SOURCE_TYPE
        , DB_CONNECTION_ID
        , DATA_SOURCE_TYPE
        , REST_API_CONNECTION_ID
        , REST_API_ENDPOINT
        , REST_API_JSON_PATH
  </insert>

  <select id="selectLayoutV1ForCopy" parameterType="map" resultType="map">
    SELECT
        *
    FROM SCREEN_LAYOUTS
    WHERE SCREEN_ID = #{screen_id}
    ORDER BY DISPLAY_ORDER ASC NULLS LAST, LAYOUT_ID ASC
  </select>

  <insert id="insertLayoutV1Copy" parameterType="map">
    INSERT INTO SCREEN_LAYOUTS (
        SCREEN_ID
        , COMPONENT_TYPE
        , COMPONENT_ID
        , PARENT_ID
        , POSITION_X
        , POSITION_Y
        , WIDTH
        , HEIGHT
        , PROPERTIES
        , DISPLAY_ORDER
    ) VALUES (
        #{screen_id}
        , #{component_type}
        , #{component_id}
        , #{parent_id}
        , #{position_x}
        , #{position_y}
        , #{width}
        , #{height}
        , #{properties}::jsonb
        , #{display_order}
    )
  </insert>

  <select id="selectLayoutV2ForCopy" parameterType="map" resultType="map">
    SELECT
        SCREEN_ID
        , COMPANY_CODE
        , LAYER_ID
        , LAYER_NAME
        , CONDITION_CONFIG
        , LAYOUT_DATA
    FROM SCREEN_LAYOUTS_V2
    WHERE SCREEN_ID = #{screen_id}
  </select>

  <insert id="insertLayoutV2Copy" parameterType="map">
    INSERT INTO SCREEN_LAYOUTS_V2 (
        SCREEN_ID
        , COMPANY_CODE
        , LAYER_ID
        , LAYER_NAME
        , CONDITION_CONFIG
        , LAYOUT_DATA
        , CREATED_DATE
        , UPDATED_DATE
    ) VALUES (
        #{screen_id}
        , #{company_code}
        , #{layer_id}
        , #{layer_name}
        , #{condition_config}::jsonb
        , #{layout_data}::jsonb
        , NOW()
        , NOW()
    )
    ON CONFLICT (SCREEN_ID, COMPANY_CODE, LAYER_ID) DO UPDATE SET
        LAYOUT_DATA = EXCLUDED.LAYOUT_DATA
        , UPDATED_DATE = NOW()
  </insert>

  <!-- ─── Layout properties update (tab references) ────────────────── -->

  <select id="selectLayoutsV1ForScreen" parameterType="map" resultType="map">
    SELECT
        LAYOUT_ID
        , PROPERTIES
    FROM SCREEN_LAYOUTS
    WHERE SCREEN_ID = #{screen_id}
      AND COMPONENT_TYPE = 'component'
      AND PROPERTIES IS NOT NULL
  </select>

  <update id="updateLayoutV1Properties" parameterType="map">
    UPDATE SCREEN_LAYOUTS
    SET
        PROPERTIES = #{properties}::jsonb
    WHERE LAYOUT_ID = #{layout_id}
  </update>

  <select id="selectLayoutsV2ForScreen" parameterType="map" resultType="map">
    SELECT
        SCREEN_ID
        , LAYER_ID
        , COMPANY_CODE
        , LAYOUT_DATA
    FROM SCREEN_LAYOUTS_V2
    WHERE SCREEN_ID = #{screen_id}
      AND LAYOUT_DATA IS NOT NULL
  </select>

  <update id="updateLayoutV2Data" parameterType="map">
    UPDATE SCREEN_LAYOUTS_V2
    SET
        LAYOUT_DATA = #{layout_data}::jsonb
        , UPDATED_DATE = NOW()
    WHERE SCREEN_ID = #{screen_id}
      AND LAYER_ID = #{layer_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
  </update>

  <!-- ─── Flow cleanup on screen delete ───────────────────────────── -->

  <select id="selectLayoutsV2ForFlowCleanup" parameterType="map" resultType="map">
    SELECT
        LAYOUT_DATA
    FROM SCREEN_LAYOUTS_V2
    WHERE SCREEN_ID = #{screen_id}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
    ORDER BY CASE WHEN COMPANY_CODE = #{company_code} THEN 0 ELSE 1 END
    LIMIT 1
  </select>

  <select id="countFlowUsageInOtherScreens" parameterType="map" resultType="int">
    SELECT COUNT(*)
    FROM SCREEN_LAYOUTS_V2 SLV
    JOIN SCREEN_DEFINITIONS SD ON SLV.SCREEN_ID = SD.SCREEN_ID
    WHERE SLV.SCREEN_ID != #{screen_id}
      AND SD.IS_ACTIVE != 'D'
      <if test="company_code != null and company_code != &quot;*&quot;">
        AND (SD.COMPANY_CODE = #{company_code} OR SD.COMPANY_CODE = '*')
      </if>
      AND (
        SLV.LAYOUT_DATA::text LIKE '%"flowId":' || #{flow_id} || '%'
        OR SLV.LAYOUT_DATA::text LIKE '%"flowId":"' || #{flow_id} || '"%'
      )
  </select>

  <delete id="deleteFlowStepConnections" parameterType="map">
    DELETE FROM FLOW_STEP_CONNECTION
    WHERE FLOW_DEFINITION_ID = #{flow_id}
  </delete>

  <delete id="deleteFlowSteps" parameterType="map">
    DELETE FROM FLOW_STEP
    WHERE FLOW_DEFINITION_ID = #{flow_id}
  </delete>

  <delete id="deleteFlowDefinition" parameterType="map">
    DELETE FROM FLOW_DEFINITION
    WHERE ID = #{flow_id}
    <if test="company_code != null and company_code != &quot;*&quot;">
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
    </if>
  </delete>

  <delete id="deleteNodeFlowsByFlowId" parameterType="map">
    DELETE FROM NODE_FLOWS
    WHERE FLOW_ID = #{flow_id}
    <if test="company_code != null and company_code != &quot;*&quot;">
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
    </if>
  </delete>

  <!-- ─── Copy operations ─────────────────────────────────────────── -->

  <select id="selectMenuAssignmentsForCopy" parameterType="map" resultType="map">
    SELECT
        *
    FROM SCREEN_MENU_ASSIGNMENTS
    WHERE SCREEN_ID IN
    <foreach collection="screenIds" item="id" open="(" separator="," close=")">
      #{id}
    </foreach>
    AND (COMPANY_CODE = #{source_company_code} OR COMPANY_CODE = '*')
    AND IS_ACTIVE = 'Y'
  </select>

  <insert id="insertMenuAssignmentCopy" parameterType="map">
    INSERT INTO SCREEN_MENU_ASSIGNMENTS (
        SCREEN_ID
        , MENU_OBJID
        , COMPANY_CODE
        , DISPLAY_ORDER
        , CREATED_BY
    ) VALUES (
        #{screen_id}
        , #{menu_objid}
        , #{company_code}
        , #{display_order}
        , #{created_by}
    )
    ON CONFLICT DO NOTHING
  </insert>

  <select id="selectCodeCategoryForCopy" parameterType="map" resultType="map">
    SELECT
        *
    FROM CODE_INFO
    WHERE (COMPANY_CODE = #{source_company_code} OR COMPANY_CODE = '*')
  </select>

  <insert id="upsertCodeCategory" parameterType="map">
    INSERT INTO CODE_INFO (
        CATEGORY_CODE
        , CATEGORY_NAME
        , COMPANY_CODE
        , DESCRIPTION
    ) VALUES (
        #{category_code}
        , #{category_name}
        , #{target_company_code}
        , #{description}
    )
    ON CONFLICT (CATEGORY_CODE, COMPANY_CODE) DO UPDATE SET
        CATEGORY_NAME = EXCLUDED.CATEGORY_NAME
        , DESCRIPTION = EXCLUDED.DESCRIPTION
  </insert>

  <select id="selectCodeInfoForCopy" parameterType="map" resultType="map">
    SELECT
        *
    FROM CODE_INFO
    WHERE (COMPANY_CODE = #{source_company_code} OR COMPANY_CODE = '*')
      AND CODE_INFO = #{code_info}
  </select>

  <insert id="upsertCodeInfo" parameterType="map">
    INSERT INTO CODE_INFO (
        CODE_INFO
        , CODE_VALUE
        , CODE_NAME
        , COMPANY_CODE
        , SORT_ORDER
        , IS_ACTIVE
    ) VALUES (
        #{code_info}
        , #{code_value}
        , #{code_name}
        , #{target_company_code}
        , #{sort_order}
        , #{is_active}
    )
    ON CONFLICT (CODE_INFO, CODE_VALUE, COMPANY_CODE) DO UPDATE SET
        CODE_NAME = EXCLUDED.CODE_NAME
        , SORT_ORDER = EXCLUDED.SORT_ORDER
        , IS_ACTIVE = EXCLUDED.IS_ACTIVE
  </insert>

  <select id="selectCategoryTreeForCopy" parameterType="map" resultType="map">
    SELECT
        *
    FROM CATEGORY_TREE
    WHERE (COMPANY_CODE = #{source_company_code} OR COMPANY_CODE = '*')
  </select>

  <insert id="upsertCategoryTree" parameterType="map">
    INSERT INTO CATEGORY_TREE (
        CATEGORY_CODE
        , CATEGORY_NAME
        , PARENT_CODE
        , COMPANY_CODE
        , SORT_ORDER
    ) VALUES (
        #{category_code}
        , #{category_name}
        , #{parent_code}
        , #{target_company_code}
        , #{sort_order}
    )
    ON CONFLICT (CATEGORY_CODE, COMPANY_CODE) DO UPDATE SET
        CATEGORY_NAME = EXCLUDED.CATEGORY_NAME
        , PARENT_CODE = EXCLUDED.PARENT_CODE
        , SORT_ORDER = EXCLUDED.SORT_ORDER
  </insert>

  <select id="selectCategoryValueForCopy" parameterType="map" resultType="map">
    SELECT
        *
    FROM CATEGORY_VALUE
    WHERE (COMPANY_CODE = #{source_company_code} OR COMPANY_CODE = '*')
  </select>

  <insert id="upsertCategoryValue" parameterType="map">
    INSERT INTO CATEGORY_VALUE (
        CATEGORY_CODE
        , VALUE_CODE
        , VALUE_NAME
        , COMPANY_CODE
        , SORT_ORDER
    ) VALUES (
        #{category_code}
        , #{value_code}
        , #{value_name}
        , #{target_company_code}
        , #{sort_order}
    )
    ON CONFLICT (CATEGORY_CODE, VALUE_CODE, COMPANY_CODE) DO UPDATE SET
        VALUE_NAME = EXCLUDED.VALUE_NAME
        , SORT_ORDER = EXCLUDED.SORT_ORDER
  </insert>

  <select id="selectTableTypeColumnsForCopy" parameterType="map" resultType="map">
    SELECT
        *
    FROM TABLE_TYPE_COLUMNS
    WHERE (COMPANY_CODE = #{source_company_code} OR COMPANY_CODE = '*')
  </select>

  <insert id="upsertTableTypeColumn" parameterType="map">
    INSERT INTO TABLE_TYPE_COLUMNS (
        TABLE_NAME
        , COLUMN_NAME
        , COLUMN_LABEL
        , INPUT_TYPE
        , COMPANY_CODE
        , IS_VISIBLE
        , DISPLAY_ORDER
        , IS_NULLABLE
    ) VALUES (
        #{table_name}
        , #{column_name}
        , #{column_label}
        , #{input_type}
        , #{target_company_code}
        , #{is_visible}
        , #{display_order}
        , #{is_nullable}
    )
    ON CONFLICT (TABLE_NAME, COLUMN_NAME, COMPANY_CODE) DO UPDATE SET
        COLUMN_LABEL = EXCLUDED.COLUMN_LABEL
        , INPUT_TYPE = EXCLUDED.INPUT_TYPE
        , IS_VISIBLE = EXCLUDED.IS_VISIBLE
        , DISPLAY_ORDER = EXCLUDED.DISPLAY_ORDER
  </insert>

  <select id="selectCascadingRelationForCopy" parameterType="map" resultType="map">
    SELECT
        *
    FROM CASCADING_RELATION
    WHERE (COMPANY_CODE = #{source_company_code} OR COMPANY_CODE = '*')
  </select>

  <insert id="upsertCascadingRelation" parameterType="map">
    INSERT INTO CASCADING_RELATION (
        PARENT_TABLE
        , PARENT_COLUMN
        , CHILD_TABLE
        , CHILD_COLUMN
        , COMPANY_CODE
    ) VALUES (
        #{parent_table}
        , #{parent_column}
        , #{child_table}
        , #{child_column}
        , #{target_company_code}
    )
    ON CONFLICT (PARENT_TABLE, PARENT_COLUMN, CHILD_TABLE, CHILD_COLUMN, COMPANY_CODE)
    DO NOTHING
  </insert>

  <!-- ─── POP deploy ──────────────────────────────────────────────── -->

  <select id="selectScreenByCode" parameterType="map" resultType="map">
    SELECT
        *
    FROM SCREEN_DEFINITIONS
    WHERE SCREEN_CODE = #{screen_code}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
      AND IS_ACTIVE != 'D'
    LIMIT 1
  </select>

  <insert id="insertScreenForDeploy" parameterType="map" useGeneratedKeys="false">
    INSERT INTO SCREEN_DEFINITIONS (
        SCREEN_NAME
        , SCREEN_CODE
        , TABLE_NAME
        , COMPANY_CODE
        , DESCRIPTION
        , CREATED_BY
        , DB_SOURCE_TYPE
        , DATA_SOURCE_TYPE
    ) VALUES (
        #{screen_name}
        , #{screen_code}
        , #{table_name}
        , #{company_code}
        , #{description}
        , #{created_by}
        , #{db_source_type}
        , #{data_source_type}
    )
    RETURNING
        SCREEN_ID
        , SCREEN_NAME
        , SCREEN_CODE
        , COMPANY_CODE
  </insert>

  <insert id="upsertPopLayoutDeploy" parameterType="map">
    INSERT INTO SCREEN_LAYOUTS_POP (
        SCREEN_ID
        , COMPANY_CODE
        , LAYOUT_DATA
        , CREATED_DATE
        , UPDATED_DATE
        , CREATED_BY
        , UPDATED_BY
    ) VALUES (
        #{screen_id}
        , #{company_code}
        , #{layout_data}::jsonb
        , NOW()
        , NOW()
        , #{user_id}
        , #{user_id}
    )
    ON CONFLICT (SCREEN_ID, COMPANY_CODE) DO UPDATE SET
        LAYOUT_DATA = EXCLUDED.LAYOUT_DATA
        , UPDATED_DATE = NOW()
        , UPDATED_BY = EXCLUDED.UPDATED_BY
  </insert>

  <!-- ─── Tables ──────────────────────────────────────────────────── -->

  <select id="selectPublicTables" parameterType="map" resultType="map">
    SELECT
        TABLE_NAME
    FROM INFORMATION_SCHEMA.TABLES
    WHERE TABLE_SCHEMA = 'public'
      AND TABLE_TYPE = 'BASE TABLE'
    ORDER BY TABLE_NAME
  </select>

  <select id="selectTableInfoByName" parameterType="map" resultType="map">
    SELECT
        TABLE_NAME
        , (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS C WHERE C.TABLE_NAME = T.TABLE_NAME) AS COLUMN_COUNT
    FROM INFORMATION_SCHEMA.TABLES T
    WHERE TABLE_SCHEMA = 'public'
      AND TABLE_TYPE = 'BASE TABLE'
      AND TABLE_NAME = #{table_name}
    LIMIT 1
  </select>

  <select id="selectTableColumnsInfo" parameterType="map" resultType="map">
    SELECT
        C.COLUMN_NAME
        , C.DATA_TYPE
        , C.IS_NULLABLE
        , C.COLUMN_DEFAULT
        , C.CHARACTER_MAXIMUM_LENGTH
        , C.NUMERIC_PRECISION
        , C.NUMERIC_SCALE
    FROM INFORMATION_SCHEMA.COLUMNS C
    WHERE C.TABLE_SCHEMA = 'public'
      AND C.TABLE_NAME = #{table_name}
    ORDER BY C.ORDINAL_POSITION
  </select>

  <select id="selectTableTypeColumnsInfo" parameterType="map" resultType="map">
    SELECT
        COLUMN_NAME
        , INPUT_TYPE
        , COLUMN_LABEL
        , CODE_INFO
        , REFERENCE_TABLE
        , REFERENCE_COLUMN
        , DISPLAY_COLUMN
        , IS_VISIBLE
        , DISPLAY_ORDER
        , DESCRIPTION
    FROM TABLE_TYPE_COLUMNS
    WHERE TABLE_NAME = #{table_name}
      AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
    ORDER BY DISPLAY_ORDER NULLS LAST, COLUMN_NAME
  </select>

</mapper>
