CREATE OR REPLACE PROCEDURE public.get_grid_items(
	IN json_input jsonb,
	INOUT result jsonb)
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
    v_record_offset INT;
    v_record_limit INT;
    v_sort_columns JSONB;
    v_search_all JSONB;
    v_search_any JSONB;
    v_select_columns JSONB;
	v_grid_params JSONB;
    v_entity_table TEXT;
    v_print_query BOOLEAN;
    v_entity_name TEXT;
    v_select_clause TEXT := '';
    v_join_clause TEXT := '';
    v_where_clause TEXT := ' WHERE ';
    v_group_by_clause TEXT := '';
    v_having_clause TEXT := ''; 
    v_query_str TEXT;
	v_count_query_str TEXT;
    v_total_records_count INT;
    v_records JSONB;
    v_headers JSONB := '[]';
    v_executed_query TEXT := '';
    v_company_id INT;
    v_group_by JSONB;
    v_having_conditions JSONB;
    v_having_any_conditions JSONB;
    
    -- Variables for iteration
    v_col JSONB;
    v_col_search JSONB;
    v_sort_col JSONB;
    v_inner_col_search JSONB;
    v_group_col TEXT;
    v_having_cond RECORD;
    
    -- To handle column aliases with "." notation
    v_alias_mapping JSONB := '{}';
    v_include_result_row RECORD;

    -- For dynamic JSON construction
    v_json_fields TEXT := '';
    v_json_object TEXT;
    v_header_object TEXT;
    
    -- For OR conditions
    v_or_clause TEXT := '';
    v_or_group TEXT := '';
    
    v_cte_clause TEXT := '';

    cunique TEXT;

    -- Security: Define valid operators
    v_valid_operators JSONB := jsonb_build_array('=', '!=', '<', '>', '<=', '>=', 'IS', 'IS NOT', 'IN', 'NOT IN', 'LIKE', 'NOT LIKE', 'ILIKE', 'NOT ILIKE', 'BETWEEN');

-- Sorting Variables
    v_sort_condition TEXT DEFAULT '';
    
	 v_query_info_search JSONB;
    v_combined_search_all JSONB;

	v_query_info_search_any JSONB;
    v_combined_search_any JSONB;
	 v_query_info_sort JSONB;
    v_final_sort_columns JSONB;
	v_user_id INT := NULL;
	input_json1 JSONB;
	input_json2 JSONB;
    replace_value_result JSONB;
	replaced_query TEXT;
BEGIN
    -- Parse the JSON input
    v_record_offset := COALESCE((json_input->>'start_index')::INT, 0);
    v_record_limit := COALESCE((json_input->>'limit_range')::INT, 10);
    v_sort_columns := (json_input->'sort_columns')::jsonb;
    v_search_all := (json_input->'search_all')::jsonb;
    v_search_any := (json_input->'search_any')::jsonb;
	v_grid_params := (json_input->'grid_params')::jsonb;
    v_entity_name := json_input->>'entity_name';
    v_company_id := COALESCE((json_input->>'company_id')::INT, 0);
    cunique := COALESCE(json_input->>'unique_id', NULL);
    -- Fetch print_query from master_entities
    SELECT COALESCE(query_information->>'print_query', 'false')::BOOLEAN INTO v_print_query
    FROM master_entities
    WHERE entity_name = v_entity_name AND company_id = v_company_id;
    -- v_print_query is now always from master_entities
    v_group_by := json_input->'group_by';
    v_having_conditions := json_input->'having_conditions';
    v_having_any_conditions := json_input->'having_any_conditions';
    v_user_id := COALESCE((json_input->>'user_id')::INT, NULL);

    -- Fetch the CTE from master_entities if available
    SELECT COALESCE(query_information->>'cte', NULL) INTO v_cte_clause
    FROM master_entities
    WHERE entity_name = v_entity_name AND company_id = v_company_id;
    -- If not found in table, fallback to input JSON
    IF v_cte_clause IS NULL OR TRIM(v_cte_clause) = '' THEN
        IF json_input ? 'cte' THEN
            v_cte_clause := json_input->>'cte';
        END IF;
    END IF;

    -- Fetch the select_columns dynamically
    v_select_columns := get_grid_columns(v_entity_name, v_company_id);

    -- Fetch the primary_table dynamically based on the entity_name
    SELECT primary_table INTO v_entity_table 
    FROM master_entities 
    WHERE entity_name = v_entity_name AND company_id = v_company_id;

    -- Build the SELECT clause
    FOR v_col IN SELECT * FROM jsonb_array_elements(v_select_columns) LOOP
        IF v_select_clause <> '' THEN
            v_select_clause := v_select_clause || ', ';
        END IF;
        v_select_clause := v_select_clause || (v_col->>0);
        IF (v_col->>1) IS NOT NULL THEN
            v_select_clause := v_select_clause || ' AS ' || quote_ident((v_col->>1));
                
                
            v_header_object := jsonb_build_object(
                'header', v_col->>1,
                'column_width', v_col->>2,
                'column_order', v_col->>2,
                'is_searchable', v_col->>3,
                'is_grid_column', v_col->>4,
                'is_sortable', v_col->>5,
                'field_value', v_col->>6,
                'clause_type', v_col->>8,
                'link_type', v_col->>9,
                'link_action', v_col->>10,
                'link_mode', v_col->>11,
                'field_type_id', v_col->>7
            )::TEXT;

            v_headers := v_headers || jsonb(v_header_object);
        END IF;
        v_alias_mapping := v_alias_mapping || jsonb_build_object(quote_ident((v_col->>1)), (v_col->>0));
    END LOOP;

    -- Build the JOIN clauses and GROUP BY clause
   -- IF cunique IS NOT NULL THEN
   -- input_json1 := jsonb_build_object('$session_user_id', v_user_id, '$unique_id', cunique);
--ELSE
    input_json1 := jsonb_build_object('$session_user_id', v_user_id);
--END IF;
    FOR v_include_result_row IN SELECT * FROM build_includes1(v_entity_name, '', '', v_company_id) LOOP
		-- Apply placeholder replacement only on current clause
		replaced_query := replace_placeholder_variables(v_include_result_row.join_clause, input_json1);
        -- Append the replaced clause
        v_join_clause := v_join_clause || ' ' || replaced_query;
        RAISE NOTICE 'v_join_clause: %', v_join_clause;
		v_alias_mapping := v_alias_mapping || v_include_result_row.alias_mapping;
        v_group_by_clause := v_include_result_row.select_clause;
        v_having_clause := v_include_result_row.having_clause;
    END LOOP;

	SELECT 
	COALESCE(query_information->>'search_all', '[]'::text)::jsonb,
	COALESCE(query_information->>'search_any', '[]'::text)::jsonb,
	 COALESCE(query_information->>'sort_columns', '[]'::text)::jsonb
    INTO v_query_info_search,v_query_info_search_any,v_query_info_sort
    FROM master_entities
    WHERE entity_name = v_entity_name 
    AND company_id = v_company_id;
    -- Combine search conditions from input and query_information
     v_combined_search_all := v_search_all || v_query_info_search;
	v_combined_search_any := v_search_any || v_query_info_search_any;

    -- IF cunique IS NOT NULL THEN
    --     v_combined_search_all := jsonb_agg(
    --         jsonb_build_object(
    --             'column_name', elem->>'column_name',
    --             'operator', elem->>'operator',
    --             'value', 
    --                 CASE 
    --                     WHEN elem->>'value' LIKE '%$unique_id%' 
    --                     THEN replace(elem->>'value', '$unique_id', cunique)
    --                     ELSE elem->>'value'
    --                 END
    --         )
    --     ) 
    --     FROM jsonb_array_elements(v_combined_search_all) AS elem;

    --     v_combined_search_any := jsonb_agg(
    --         jsonb_build_object(
    --             'column_name', elem->>'column_name',
    --             'operator', elem->>'operator',
    --             'value', 
    --                 CASE 
    --                     WHEN elem->>'value' LIKE '%$unique_id%' 
    --                     THEN replace(elem->>'value', '$unique_id', cunique)
    --                     ELSE elem->>'value'
    --                 END
    --         )
    --     ) 
    --     FROM jsonb_array_elements(v_combined_search_any) AS elem;
    -- END IF;

    -- Build the WHERE clause for column-specific search
    v_where_clause := v_where_clause || v_entity_table || '.company_id' || ' = ' || quote_literal(v_company_id);
    -- Handle the "search_all" conditions with AND
    FOR v_col_search IN SELECT * FROM jsonb_array_elements(v_combined_search_all) LOOP
        -- Security check for column name and operator
        -- IF (v_col_search->>'column_name') !~ '^[a-zA-Z0-9_\\. ()'',:]+$' THEN
         IF (v_col_search->>'column_name') !~ '^[a-zA-Z0-9_\\. ()'',:|\\-]+$' THEN
            RAISE EXCEPTION 'Invalid characters in column name: %', (v_col_search->>'column_name');
        END IF;
        IF NOT v_valid_operators ? (v_col_search->>'operator') THEN
            RAISE EXCEPTION 'Invalid operator: %', (v_col_search->>'operator');
        END IF;
        -- Construct the condition
        IF (v_col_search->>'operator') = 'IN' OR (v_col_search->>'operator') = 'NOT IN' THEN
            v_where_clause := v_where_clause || ' AND ' || (v_col_search->>'column_name') || ' ' || (v_col_search->>'operator') || ' (' || array_to_string(ARRAY(SELECT quote_literal(trim(both '''' FROM x)) FROM jsonb_array_elements_text(v_col_search->'value') x), ', ') || ')';
        ELSIF (v_col_search->>'operator') = 'BETWEEN' THEN
            v_where_clause := v_where_clause || ' AND ' || (v_col_search->>'column_name') || ' BETWEEN ' || quote_literal((v_col_search->'value')->>0) || ' AND ' || quote_literal((v_col_search->'value')->>1);
        ELSE
            IF (v_col_search->>'value') IS NOT NULL THEN 
				  input_json2 := jsonb_build_object(
				    'placeholder', (v_col_search->>'value'),  -- The placeholder to look for
				    'value_json', jsonb_build_object('$session_user_id', v_user_id)  -- Add v_user_id to the JSON object
				  );
				  
				  -- Call the replace_values function
				  replace_value_result := replace_values(input_json2);
                v_where_clause := v_where_clause || ' AND ' || (v_col_search->>'column_name') || ' ' || (v_col_search->>'operator') || ' ' || quote_literal((replace_value_result->>'value'));
            ELSIF (v_col_search->>'operator' = 'IS' OR v_col_search->>'operator' = 'IS NOT') THEN
                v_where_clause := v_where_clause || ' AND ' || (v_col_search->>'column_name') || ' ' || (v_col_search->>'operator') || ' ' || 'NULL';
            END IF;
        END IF;
    END LOOP;

    -- Handle the "search_any" conditions with nested OR
    FOR v_col_search IN SELECT * FROM jsonb_array_elements(v_combined_search_any) LOOP
        IF jsonb_typeof(v_col_search) = 'array' THEN
            -- Multiple sets of conditions
            v_or_group := '';
            FOR v_inner_col_search IN SELECT * FROM jsonb_array_elements(v_col_search) LOOP
                -- Security check for column name and operator
                --IF (v_inner_col_search->>'column_name') !~ '^[a-zA-Z0-9_\\. ()'',:]+$' THEN
                 IF (v_inner_col_search->>'column_name') !~ '^[a-zA-Z0-9_\\. ()'',:|\\-]+$' THEN
                    RAISE EXCEPTION 'Invalid characters in column name: %', (v_inner_col_search->>'column_name');
                END IF;
                IF NOT v_valid_operators ? (v_inner_col_search->>'operator') THEN
                    RAISE EXCEPTION 'Invalid operator: %', (v_inner_col_search->>'operator');
                END IF;
                -- Construct the condition
                IF (v_inner_col_search->>'operator') = 'IN' OR (v_inner_col_search->>'operator') = 'NOT IN' THEN
                    v_or_group := v_or_group || (v_inner_col_search->>'column_name') || ' ' || (v_inner_col_search->>'operator') || ' (' || array_to_string(ARRAY(SELECT quote_literal(trim(both '''' FROM x)) FROM jsonb_array_elements_text(v_inner_col_search->'value') x), ', ') || ') OR ';
                ELSIF (v_inner_col_search->>'operator') = 'BETWEEN' THEN
                    v_or_group := v_or_group || (v_inner_col_search->>'column_name') || ' BETWEEN ' || quote_literal((v_inner_col_search->'value')->>0) || ' AND ' || quote_literal((v_inner_col_search->'value')->>1) || ' OR ';
                ELSE
                    IF (v_inner_col_search->>'value') IS NOT NULL THEN 
						input_json2 := jsonb_build_object(
							'placeholder', (v_inner_col_search->>'value'),  -- The placeholder to look for
							'value_json', jsonb_build_object('$session_user_id', v_user_id)  -- Add v_user_id to the JSON object
						  );
						  
						  -- Call the replace_values function
						  replace_value_result := replace_values(input_json2);
                        v_or_group := v_or_group || (v_inner_col_search->>'column_name') || ' ' || (v_inner_col_search->>'operator') || ' ' || quote_literal((replace_value_result->>'value')) || ' OR ';
                    ELSIF (v_inner_col_search->>'operator' = 'IS' OR v_inner_col_search->>'operator' = 'IS NOT') THEN
                        v_or_group := v_or_group || (v_inner_col_search->>'column_name') || ' ' || (v_inner_col_search->>'operator') || ' ' || 'NULL' || ' OR ';
                    END IF;
                END IF;
            END LOOP;
            -- Remove the trailing ' OR ' and add to the OR clause
            IF v_or_group <> '' THEN
                v_or_group := LEFT(v_or_group, LENGTH(v_or_group) - 4);
                v_or_clause := v_or_clause || '(' || v_or_group || ')' || ' AND ';
            END IF;
        ELSE
            -- Single set of conditions
            -- Security check for column name and operator
           -- IF (v_col_search->>'column_name') !~ '^[a-zA-Z0-9_\\. ()'',:]+$' THEN
            IF (v_col_search->>'column_name') !~ '^[a-zA-Z0-9_\\. ()'',:|\\-]+$' THEN
                RAISE EXCEPTION 'Invalid characters in column name: %', (v_col_search->>'column_name');
            END IF;
            IF NOT v_valid_operators ? (v_col_search->>'operator') THEN
                RAISE EXCEPTION 'Invalid operator: %', (v_col_search->>'operator');
            END IF;
            -- Construct the condition
            IF (v_col_search->>'operator') = 'IN' OR (v_col_search->>'operator') = 'NOT IN' THEN
                v_or_clause := v_or_clause || (v_col_search->>'column_name') || ' ' || (v_col_search->>'operator') || ' (' || array_to_string(ARRAY(SELECT quote_literal(trim(both '''' FROM x)) FROM jsonb_array_elements_text(v_col_search->'value') x), ', ') || ') OR ';
            ELSIF (v_col_search->>'operator') = 'BETWEEN' THEN
                v_or_clause := v_or_clause || (v_col_search->>'column_name') || ' BETWEEN ' || quote_literal((v_col_search->'value')->>0) || ' AND ' || quote_literal((v_col_search->'value')->>1) || ' OR ';
            ELSE
                IF (v_col_search->>'value') IS NOT NULL THEN 
					input_json2 := jsonb_build_object(
							'placeholder', (v_col_search->>'value'),  -- The placeholder to look for
							'value_json', jsonb_build_object('$session_user_id', v_user_id)  -- Add v_user_id to the JSON object
						  );
						  
					-- Call the replace_values function
					replace_value_result := replace_values(input_json2);
                    v_or_clause := v_or_clause || (v_col_search->>'column_name') || ' ' || (v_col_search->>'operator') || ' ' || quote_literal((replace_value_result->>'value')) || ' OR ';
                ELSIF (v_col_search->>'operator' = 'IS' OR v_col_search->>'operator' = 'IS NOT') THEN
                    v_or_clause := v_or_clause || (v_col_search->>'column_name') || ' ' || (v_col_search->>'operator') || ' ' || 'NULL' || ' OR ';
                END IF;
            END IF;
        END IF;
    END LOOP;

    -- Remove the trailing ' OR ' and ' AND ' and add to the where clause
    IF v_or_clause <> '' THEN
        v_or_clause := LEFT(v_or_clause, LENGTH(v_or_clause) - 4);
        v_where_clause := v_where_clause || ' AND (' || v_or_clause || ')';
    END IF;

    -- Build the GROUP BY clause
    IF v_group_by IS NOT NULL THEN
        FOR v_group_col IN SELECT * FROM jsonb_array_elements_text(v_group_by) LOOP
		    IF v_group_by_clause = '' THEN
		        v_group_by_clause := ' GROUP BY ';
		    END IF;
            IF v_group_by_clause <> ' GROUP BY ' THEN
                v_group_by_clause := v_group_by_clause || ', ';
            END IF;
            v_group_by_clause := v_group_by_clause || v_group_col;
        END LOOP;
    END IF;

	-- Build the HAVING clause
    IF v_having_conditions IS NOT NULL THEN
		IF v_having_clause = '' THEN
			v_having_clause := ' HAVING ';
		END IF;
        FOR v_having_cond IN SELECT * FROM jsonb_array_elements(v_having_conditions) LOOP
            -- Security check for column name and operator
            --IF (v_having_cond.value->>'column_name') !~ '^[a-zA-Z0-9_\. ()'',:]+$' THEN
            IF (v_having_cond.value->>'column_name') !~ '^[a-zA-Z0-9_\\. ()'',:|\\-]+$' THEN
                RAISE EXCEPTION 'Invalid characters in column name in HAVING: %', (v_having_cond.value->>'column_name');
            END IF;
            IF NOT v_valid_operators ? (v_having_cond.value->>'operator') THEN
                RAISE EXCEPTION 'Invalid operator in HAVING: %', (v_having_cond.value->>'operator');
            END IF;
            -- Construct the condition
            IF (v_having_cond.value->>'operator') = 'IN' OR (v_having_cond.value->>'operator') = 'NOT IN' THEN
                v_having_clause := v_having_clause || (v_having_cond.value->>'column_name') || ' ' || (v_having_cond.value->>'operator') || ' (' || array_to_string(ARRAY(SELECT quote_literal(trim(both '''' FROM x)) FROM jsonb_array_elements_text(v_having_cond.value->'value') x), ', ') || ') AND ';
            ELSIF (v_having_cond.value->>'operator') = 'BETWEEN' THEN
                v_having_clause := v_having_clause || (v_having_cond.value->>'column_name') || ' BETWEEN ' || quote_literal((v_having_cond.value->'value')->>0) || ' AND ' || quote_literal((v_having_cond.value->'value')->>1) || ' AND ';
            ELSE
                IF (v_having_cond.value->>'value') IS NOT NULL THEN 
					input_json2 := jsonb_build_object(
							'placeholder', (v_having_cond.value->>'value'),  -- The placeholder to look for
							'value_json', jsonb_build_object('$session_user_id', v_user_id)  -- Add v_user_id to the JSON object
						  );
						  
					-- Call the replace_values function
					replace_value_result := replace_values(input_json2);
                    v_having_clause := v_having_clause || (v_having_cond.value->>'column_name') || ' ' || (v_having_cond.value->>'operator') || ' ' || quote_literal((replace_value_result->>'value')) || ' AND ';
                ELSIF (v_having_cond.value->>'operator' = 'IS' OR v_having_cond.value->>'operator' = 'IS NOT') THEN

                    v_having_clause := v_having_clause || (v_having_cond.value->>'column_name') || ' ' || (v_having_cond.value->>'operator') || ' ' || 'NULL' || ' AND ';
                END IF;
            END IF;
        END LOOP;
        -- Remove the trailing ' AND '
        IF v_having_clause <> '' THEN
            v_having_clause := LEFT(v_having_clause, LENGTH(v_having_clause) - 4);
        END IF;
    END IF;

    -- Build the HAVING any clause
    IF v_having_any_conditions IS NOT NULL THEN
        IF v_having_clause = '' THEN
            v_having_clause := ' HAVING ';
        ELSE
            v_having_clause := v_having_clause || ' AND (';
        END IF;
        FOR v_having_cond IN SELECT * FROM jsonb_array_elements(v_having_any_conditions) LOOP
            -- Security check for column name and operator
            --IF (v_having_cond.value->>'column_name') !~ '^[a-zA-Z0-9_\. ()'',:]+$' THEN
            IF (v_having_cond.value->>'column_name') !~ '^[a-zA-Z0-9_\\. ()'',:|\\-]+$' THEN
                RAISE EXCEPTION 'Invalid characters in column name in HAVING: %', (v_having_cond.value->>'column_name');
            END IF;
            IF NOT v_valid_operators ? (v_having_cond.value->>'operator') THEN
                RAISE EXCEPTION 'Invalid operator in HAVING: %', (v_having_cond.value->>'operator');
            END IF;
            -- Construct the condition
            IF (v_having_cond.value->>'operator') = 'IN' OR (v_having_cond.value->>'operator') = 'NOT IN' THEN
                v_having_clause := v_having_clause || (v_having_cond.value->>'column_name') || ' ' || (v_having_cond.value->>'operator') || ' (' || array_to_string(ARRAY(SELECT quote_literal(trim(both '''' FROM x)) FROM jsonb_array_elements_text(v_having_cond.value->'value') x), ', ') || ') OR ';
            ELSIF (v_having_cond.value->>'operator') = 'BETWEEN' THEN
                v_having_clause := v_having_clause || (v_having_cond.value->>'column_name') || ' BETWEEN ' || quote_literal((v_having_cond.value->'value')->>0) || ' AND ' || quote_literal((v_having_cond.value->'value')->>1) || ' OR ';
            ELSE
                IF (v_having_cond.value->>'value') IS NOT NULL THEN 
                    input_json2 := jsonb_build_object(
                            'placeholder', (v_having_cond.value->>'value'),  -- The placeholder to look for
                            'value_json', jsonb_build_object('$session_user_id', v_user_id)  -- Add v_user_id to the JSON object
                          );
                          
                    -- Call the replace_values function
                    replace_value_result := replace_values(input_json2);
                    v_having_clause := v_having_clause || (v_having_cond.value->>'column_name') || ' ' || (v_having_cond.value->>'operator') || ' ' || quote_literal((replace_value_result->>'value')) || ' OR ';
                ELSIF (v_having_cond.value->>'operator' = 'IS' OR v_having_cond.value->>'operator' = 'IS NOT') THEN
                    v_having_clause := v_having_clause || (v_having_cond.value->>'column_name') || ' ' || (v_having_cond.value->>'operator') || ' ' || 'NULL' || ' OR ';
                END IF;
            END IF;
        END LOOP;
        -- Remove the trailing ' OR '
        IF v_having_clause <> '' THEN
            v_having_clause := LEFT(v_having_clause, LENGTH(v_having_clause) - 3);
        END IF;
        IF v_having_conditions IS NOT NULL THEN
            v_having_clause := v_having_clause || ')';
        END IF;
    END IF;
    RAISE NOTICE 'HAVING Clause1: %', v_having_clause;

    -- Handle CTE if present
    IF json_input ? 'cte' THEN
        v_cte_clause := json_input->>'cte';
    END IF;

    -- Build the base query (without limit/offset/order yet)
    v_query_str := 'SELECT ' || v_select_clause || ' FROM ' || v_entity_table || v_join_clause || v_where_clause || v_group_by_clause || v_having_clause;

    -- Prepend CTE if present
    --IF v_cte_clause IS NOT NULL AND TRIM(v_cte_clause) <> '' THEN
      --  v_query_str := v_cte_clause || ' ' || v_query_str;
    --END IF;

    -- Total records count
    IF v_cte_clause IS NOT NULL AND TRIM(v_cte_clause) <> '' THEN
    v_count_query_str := v_cte_clause || ' SELECT COUNT(*) FROM (SELECT 1 FROM ' || v_entity_table || v_join_clause || v_where_clause || v_group_by_clause || v_having_clause || ') AS subquery';
ELSE
    v_count_query_str := 'SELECT COUNT(*) FROM (SELECT 1 FROM ' || v_entity_table || v_join_clause || v_where_clause || v_group_by_clause || v_having_clause || ') AS subquery';
END IF;


v_count_query_str := replace_placeholder_variables(v_count_query_str, v_grid_params);

	EXECUTE v_count_query_str INTO v_total_records_count;

    --IF v_cte_clause IS NOT NULL AND TRIM(v_cte_clause) <> '' THEN
      --  EXECUTE v_cte_clause || ' SELECT COUNT(*) FROM (SELECT 1 FROM ' || v_entity_table || v_join_clause || v_where_clause || v_group_by_clause || v_having_clause || ') AS subquery' INTO v_total_records_count;
    --ELSE
      --  EXECUTE 'SELECT COUNT(*) FROM (SELECT 1 FROM ' || v_entity_table || v_join_clause || v_where_clause || v_group_by_clause || v_having_clause || ') AS subquery' INTO v_total_records_count;
    --END IF;

    -- Determine final sort columns
    IF jsonb_array_length(v_sort_columns) > 0 THEN
        v_final_sort_columns := v_sort_columns;
    ELSIF jsonb_array_length(v_query_info_sort) > 0 THEN
        v_final_sort_columns := v_query_info_sort;
    ELSE
        v_final_sort_columns := '[]'::jsonb;
    END IF;

    IF v_final_sort_columns IS NOT NULL THEN
        FOR v_sort_col IN SELECT * FROM jsonb_array_elements(v_final_sort_columns) LOOP
            -- Validate column names to prevent SQL injection
            IF (v_sort_col->>0) !~ '^[a-zA-Z0-9_\\. ()'',:]+$' THEN
                RAISE EXCEPTION 'Invalid column name in sort: %', (v_sort_col->>0);
            END IF;
            IF v_sort_condition = '' THEN
                v_sort_condition := (v_sort_col->>0) || ' ' || (v_sort_col->>1);
            ELSE
                v_sort_condition := v_sort_condition || ', ' || (v_sort_col->>0) || ' ' || (v_sort_col->>1);
            END IF;
        END LOOP;

        -- Append ORDER BY clause only once
        IF v_sort_condition <> '' THEN
            v_query_str := v_query_str || ' ORDER BY ' || v_sort_condition;
        END IF;
    END IF;

    -- Add limit/offset
    --v_query_str := v_query_str || ' LIMIT ' || v_record_limit || ' OFFSET ' || v_record_offset;
    v_query_str := v_query_str || ' LIMIT ' || v_record_limit || ' OFFSET ' || v_record_offset;

    

    -- Prepend CTE to the final query string if present (for both execution and query_executed)
    --IF v_cte_clause IS NOT NULL AND TRIM(v_cte_clause) <> '' THEN
      --  v_query_str := v_cte_clause || ' ' || v_query_str;
    --END IF;

    IF v_cte_clause IS NOT NULL AND TRIM(v_cte_clause) <> '' THEN
    v_query_str := v_cte_clause || ' ' || v_query_str;
END IF;

	-- Replace all variables with its respective values here (For example $gparam_1,$gparam_2..etc.)
	v_query_str := replace_placeholder_variables(v_query_str, v_grid_params);

    -- Store the executed query if print_query is true
    IF v_print_query THEN
        v_executed_query := v_query_str;
    END IF;

    -- Return empty records array since we want plain SELECT
    v_records := '[]'::jsonb;



-- Build the records JSON manually
FOR v_include_result_row IN EXECUTE v_query_str LOOP
    v_json_object := row_to_json(v_include_result_row)::text;
    v_records := v_records || v_json_object::jsonb;
END LOOP;

  IF v_print_query THEN
    v_executed_query := v_query_str;
    END IF;
    -- Set the output result
    IF v_print_query THEN
        result := jsonb_build_object(
            'total_records_count', v_total_records_count,
            'records', v_records,
            'headers', v_headers,
            'query_executed', v_executed_query,
            'error_type', '',
            'error_message', '',
            'execution_status', TRUE
        );
    ELSE
        result := jsonb_build_object(
            'total_records_count', v_total_records_count,
            'records', v_records,
            'headers', v_headers,
            'error_type', '',
            'error_message', '',
            'execution_status', TRUE
        );
    END IF;
EXCEPTION
    WHEN OTHERS THEN
        IF v_print_query THEN
            result := jsonb_build_object(
                'total_records_count', 0,
                'records', jsonb '[]',
                'query_executed', v_executed_query,
                'error_type', SQLSTATE,
                'error_message', SQLERRM,
                'execution_status', FALSE
            );
        ELSE
            result := jsonb_build_object(
                'total_records_count', 0,
                'records', jsonb '[]',
                'error_type', SQLSTATE,
                'error_message', SQLERRM,
                'execution_status', FALSE
            );
        END IF;
END;
$BODY$;

ALTER PROCEDURE public.get_grid_items(jsonb, jsonb)
    OWNER TO postgres;