4545 safeFormatFieldLiteral = regexp .MustCompile (`^[A-Za-z0-9_.-]+$` )
4646)
4747
48+ const constantSelectBasePipeline = "* | limit 1 | delete *"
49+
4850type selectTranslatorVisitor struct {
4951 result string
5052 err error
@@ -69,6 +71,7 @@ type selectTranslatorVisitor struct {
6971 constantFieldCount int
7072 aggTempDeletes map [string ]string
7173 aggPreserve map [string ]struct {}
74+ constantBase bool
7275}
7376
7477type tableBinding struct {
@@ -194,6 +197,7 @@ func (v *selectTranslatorVisitor) translateSimpleSelect(stmt *ast.SelectStatemen
194197 v .constantFieldCount = 0
195198 v .aggTempDeletes = nil
196199 v .aggPreserve = nil
200+ v .constantBase = false
197201
198202 joinPipes , err := v .processFrom (stmt .From )
199203 if err != nil {
@@ -433,11 +437,14 @@ func (v *selectTranslatorVisitor) buildDistinctPipe(fields []string, aggregated
433437
434438func (v * selectTranslatorVisitor ) processFrom (from ast.TableExpr ) ([]string , error ) {
435439 if from == nil {
436- return nil , & TranslationError {
437- Code : http .StatusBadRequest ,
438- Message : "translator: FROM clause is required" ,
439- }
440+ v .baseAlias = ""
441+ v .baseUsesPipeline = true
442+ v .basePipeline = constantSelectBasePipeline
443+ v .baseFilter = ""
444+ v .constantBase = true
445+ return nil , nil
440446 }
447+ v .constantBase = false
441448
442449 switch t := from .(type ) {
443450 case * ast.TableName :
@@ -1288,7 +1295,7 @@ func (v *selectTranslatorVisitor) prepareGroupByField(expr ast.Expr, index int)
12881295 return "" , nil , err
12891296 }
12901297 return aliasName , []string {mathPipe }, nil
1291- case * ast.BinaryExpr , * ast.UnaryExpr , * ast.NumericLiteral :
1298+ case * ast.BinaryExpr , * ast.UnaryExpr , * ast.NumericLiteral , * ast. StringLiteral :
12921299 alias := fmt .Sprintf ("group_%d" , index + 1 )
12931300 mathPipe , aliasName , err := v .translateMathProjection (expr , alias )
12941301 if err != nil {
@@ -2436,7 +2443,78 @@ func (v *selectTranslatorVisitor) buildProjectionPipes(columns []ast.SelectItem,
24362443 }
24372444 computedPipes = append (computedPipes , mathPipe )
24382445 fields = append (fields , formatFieldName (aliasName ))
2439- case * ast.BinaryExpr , * ast.UnaryExpr , * ast.NumericLiteral :
2446+ case * ast.NumericLiteral :
2447+ if aggregated {
2448+ groupField , ok , err := v .lookupGroupExpr (col .Expr )
2449+ if err != nil {
2450+ return nil , nil , err
2451+ }
2452+ if ! ok {
2453+ return nil , nil , & TranslationError {
2454+ Code : http .StatusBadRequest ,
2455+ Message : fmt .Sprintf ("translator: unsupported expression %T in aggregated select" , expr ),
2456+ }
2457+ }
2458+ finalName := groupField
2459+ if alias := strings .TrimSpace (col .Alias ); alias != "" {
2460+ formattedAlias := formatFieldName (alias )
2461+ if formattedAlias != groupField {
2462+ renamePairs = append (renamePairs , fmt .Sprintf ("%s as %s" , groupField , formattedAlias ))
2463+ }
2464+ finalName = formattedAlias
2465+ }
2466+ fields = append (fields , finalName )
2467+ continue
2468+ }
2469+ aliasTrim := strings .TrimSpace (col .Alias )
2470+ if aliasTrim == "" && v .constantBase {
2471+ computedPipes = append (computedPipes , fmt .Sprintf ("format %s" , expr .Value ))
2472+ continue
2473+ }
2474+ aliasName , err := makeProjectionAlias (aliasTrim , "literal" , expr .Value )
2475+ if err != nil {
2476+ return nil , nil , err
2477+ }
2478+ pipe := fmt .Sprintf ("format %s as %s" , expr .Value , formatFieldName (aliasName ))
2479+ computedPipes = append (computedPipes , pipe )
2480+ fields = append (fields , formatFieldName (aliasName ))
2481+ case * ast.StringLiteral :
2482+ if aggregated {
2483+ groupField , ok , err := v .lookupGroupExpr (col .Expr )
2484+ if err != nil {
2485+ return nil , nil , err
2486+ }
2487+ if ! ok {
2488+ return nil , nil , & TranslationError {
2489+ Code : http .StatusBadRequest ,
2490+ Message : fmt .Sprintf ("translator: unsupported expression %T in aggregated select" , expr ),
2491+ }
2492+ }
2493+ finalName := groupField
2494+ if alias := strings .TrimSpace (col .Alias ); alias != "" {
2495+ formattedAlias := formatFieldName (alias )
2496+ if formattedAlias != groupField {
2497+ renamePairs = append (renamePairs , fmt .Sprintf ("%s as %s" , groupField , formattedAlias ))
2498+ }
2499+ finalName = formattedAlias
2500+ }
2501+ fields = append (fields , finalName )
2502+ continue
2503+ }
2504+ aliasTrim := strings .TrimSpace (col .Alias )
2505+ value := quoteString (expr .Value )
2506+ if aliasTrim == "" && v .constantBase {
2507+ computedPipes = append (computedPipes , fmt .Sprintf ("format %s" , value ))
2508+ continue
2509+ }
2510+ aliasName , err := makeProjectionAlias (aliasTrim , "literal" , expr .Value )
2511+ if err != nil {
2512+ return nil , nil , err
2513+ }
2514+ pipe := fmt .Sprintf ("format %s as %s" , value , formatFieldName (aliasName ))
2515+ computedPipes = append (computedPipes , pipe )
2516+ fields = append (fields , formatFieldName (aliasName ))
2517+ case * ast.BinaryExpr , * ast.UnaryExpr :
24402518 if aggregated {
24412519 groupField , ok , err := v .lookupGroupExpr (col .Expr )
24422520 if err != nil {
0 commit comments