Copilot commented on code in PR #114:
URL: https://github.com/apache/iotdb-extras/pull/114#discussion_r3618972947


##########
connectors/grafana-plugin/pkg/plugin/table_query.go:
##########
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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 plugin
+
+import (
+       "bytes"
+       "context"
+       "encoding/json"
+       "errors"
+       "io"
+       "net/http"
+       "regexp"
+       "sort"
+       "strconv"
+       "strings"
+       "time"
+
+       "github.com/grafana/grafana-plugin-sdk-go/backend"
+       "github.com/grafana/grafana-plugin-sdk-go/backend/log"
+       "github.com/grafana/grafana-plugin-sdk-go/data"
+)
+
+// TableModelSqlType is the QueryEditor mode that sends standard table-model 
SQL
+// straight to IoTDB's table REST interface, as opposed to the tree-model modes
+// ("SQL: Full Customized" / "SQL: Drop-down List") that build root.* paths.
+const TableModelSqlType = "SQL: Table Model"
+
+// Result formats for the table-model mode. Time series (the default) sorts
+// rows ascending by the first TIMESTAMP column and pivots a long-shaped result
+// (time + tag columns + value columns) into one labeled series per tag
+// combination; Table returns the rows exactly as the server sent them.
+const (
+       tableFormatTimeSeries = "Time series"
+       tableFormatTable      = "Table"
+)
+
+// tableQueryPath is IoTDB's table-model query endpoint (see the /rest/table/v1
+// RestApi). It accepts a standard SQL statement plus an optional database
+// context and returns a column-major QueryDataSet.

Review Comment:
   The comment says the table REST endpoint returns a "column-major 
QueryDataSet", but this file (and the tests) treat the response as row-major 
(values[row][col]). The comment should be corrected to avoid misleading future 
maintainers.



##########
connectors/grafana-plugin/src/QueryEditor.tsx:
##########
@@ -247,14 +279,30 @@ export class QueryEditor extends PureComponent<Props, 
State> {
                       control: '',
                     });
                     onChange({ ...query, sqlType: value, isDropDownList: true 
});
+                  } else {
+                    this.props.query.sqlType = selectType[2];
+                    this.props.query.expression = [''];
+                    this.props.query.prefixPath = [''];
+                    this.props.query.condition = '';
+                    this.props.query.control = '';
+                    this.props.query.isDropDownList = false;
+                    this.setState({
+                      isDropDownList: false,
+                      sqlType: selectType[2],
+                      expression: [''],
+                      prefixPath: [''],
+                      condition: '',
+                      control: '',
+                    });
+                    onChange({ ...query, sqlType: value, isDropDownList: false 
});
                   }

Review Comment:
   This branch mutates `this.props.query` directly 
(sqlType/expression/prefixPath/condition/control/isDropDownList). Mutating 
props can lead to stale renders and makes state/query synchronization harder; 
prefer constructing the next query object and passing it to `onChange` without 
writing into `this.props.query`.



##########
connectors/grafana-plugin/pkg/plugin/table_query.go:
##########
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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 plugin
+
+import (
+       "bytes"
+       "context"
+       "encoding/json"
+       "errors"
+       "io"
+       "net/http"
+       "regexp"
+       "sort"
+       "strconv"
+       "strings"
+       "time"
+
+       "github.com/grafana/grafana-plugin-sdk-go/backend"
+       "github.com/grafana/grafana-plugin-sdk-go/backend/log"
+       "github.com/grafana/grafana-plugin-sdk-go/data"
+)
+
+// TableModelSqlType is the QueryEditor mode that sends standard table-model 
SQL
+// straight to IoTDB's table REST interface, as opposed to the tree-model modes
+// ("SQL: Full Customized" / "SQL: Drop-down List") that build root.* paths.
+const TableModelSqlType = "SQL: Table Model"
+
+// Result formats for the table-model mode. Time series (the default) sorts
+// rows ascending by the first TIMESTAMP column and pivots a long-shaped result
+// (time + tag columns + value columns) into one labeled series per tag
+// combination; Table returns the rows exactly as the server sent them.
+const (
+       tableFormatTimeSeries = "Time series"
+       tableFormatTable      = "Table"
+)
+
+// tableQueryPath is IoTDB's table-model query endpoint (see the /rest/table/v1
+// RestApi). It accepts a standard SQL statement plus an optional database
+// context and returns a column-major QueryDataSet.
+const tableQueryPath = "/rest/table/v1/query"
+
+// tableQueryReq is the request body for tableQueryPath. Field names match the
+// generated table/v1 SQL model (database / sql / row_limit).
+type tableQueryReq struct {
+       Database string `json:"database,omitempty"`
+       Sql      string `json:"sql"`
+       RowLimit *int   `json:"row_limit,omitempty"`
+}
+
+// tableQueryDataSet mirrors the table/v1 QueryDataSet response. Its values are
+// ROW-major (values[row][col]) because the table endpoint transposes before
+// serializing, unlike the tree-model endpoints. snake_case JSON. On failure
+// IoTDB returns an ExecutionStatus instead, whose code/message are captured
+// here so a non-zero code surfaces as an error.
+type tableQueryDataSet struct {
+       ColumnNames []string        `json:"column_names"`
+       DataTypes   []string        `json:"data_types"`
+       Values      [][]interface{} `json:"values"`
+       Code        int32           `json:"code"`
+       Message     string          `json:"message"`
+}
+
+// timeFilterRe matches Grafana's $__timeFilter(column) macro; the column is
+// optional and defaults to "time". One level of nested parentheses is allowed
+// so expressions like $__timeFilter(cast(x)) survive intact.
+var timeFilterRe = 
regexp.MustCompile(`\$__timeFilter\(\s*((?:[^()]|\([^()]*\))*?)\s*\)`)
+
+// timeFromRe / timeToRe match $__timeFrom / $__timeTo, with or without the
+// trailing () that Grafana's SQL data sources use ($__timeFrom()).
+var (
+       timeFromRe = regexp.MustCompile(`\$__timeFrom\b(?:\s*\(\s*\))?`)
+       timeToRe   = regexp.MustCompile(`\$__timeTo\b(?:\s*\(\s*\))?`)
+)
+
+// timestampUnits maps the datasource's timestampPrecision option (which must
+// match the server's timestamp_precision property) to conversion factors:
+// unitsPerMs scales the panel's epoch-ms range into server units for the time
+// macros, nsPerUnit scales raw TIMESTAMP values into nanoseconds for Grafana.
+func timestampUnits(precision string) (unitsPerMs int64, nsPerUnit int64) {
+       switch strings.TrimSpace(strings.ToLower(precision)) {
+       case "us":
+               return 1000, int64(time.Microsecond)
+       case "ns":
+               return 1000000, 1
+       default: // ms is IoTDB's default timestamp_precision
+               return 1, int64(time.Millisecond)
+       }
+}
+
+// expandTableMacros rewrites the Grafana time macros a dashboard author can 
put
+// in table-model SQL into concrete bounds for the panel's range:
+//
+//     $__timeFilter(col)      -> (col >= <from> AND col <= <to>)
+//     $__timeFrom[()]         -> <from>
+//     $__timeTo[()]           -> <to>
+//
+// Bounds are epoch values in the server's timestamp precision (milliseconds
+// unless the datasource says otherwise), matching how IoTDB compares integer
+// literals against TIMESTAMP columns.
+func expandTableMacros(sql string, start int64, end int64) string {
+       from := strconv.FormatInt(start, 10)
+       to := strconv.FormatInt(end, 10)
+       sql = timeFilterRe.ReplaceAllStringFunc(sql, func(m string) string {
+               col := strings.TrimSpace(timeFilterRe.FindStringSubmatch(m)[1])
+               if col == "" {
+                       col = "time"
+               }
+               return "(" + col + " >= " + from + " AND " + col + " <= " + to 
+ ")"
+       })
+       sql = timeFromRe.ReplaceAllString(sql, from)
+       sql = timeToRe.ReplaceAllString(sql, to)
+       return sql
+}
+
+// queryTableModel runs a table-model SQL query against IoTDB's table REST
+// endpoint and turns the column-major QueryDataSet into a Grafana data frame.
+func (d *IoTDBDataSource) queryTableModel(ctx context.Context, qp *queryParam, 
authorization string) backend.DataResponse {

Review Comment:
   This comment refers to a "column-major QueryDataSet", but the table-model 
response is handled as row-major (values[row][col]) throughout this file. Align 
the wording with the actual orientation to prevent confusion.



##########
connectors/grafana-plugin/src/datasource.ts:
##########
@@ -126,6 +126,13 @@ export class DataSource extends 
DataSourceWithBackend<IoTDBQuery, IoTDBOptions>
       if (query.fillClauses) {
         query.fillClauses = getTemplateSrv().replace(query.fillClauses, 
scopedVars);
       }
+    } else if (query.sqlType === 'SQL: Table Model') {
+      if (query.sql) {
+        query.sql = getTemplateSrv().replace(query.sql, scopedVars);
+      }
+      if (query.database) {
+        query.database = getTemplateSrv().replace(query.database, scopedVars);
+      }

Review Comment:
   The new `SQL: Table Model` branch in `applyTemplateVariables` adds 
templating behavior for `sql` and `database`, but there are no corresponding 
unit tests (existing tests cover the other modes). Adding a Jest test case 
would prevent regressions in macro/template expansion for table-model queries.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to