1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2025-01-24 22:32:42 +02:00

Merge branch 'master' into develop

This commit is contained in:
Gani Georgiev 2023-10-05 09:33:46 +03:00
commit 5a5125383a
3 changed files with 57 additions and 47 deletions

View File

@ -402,6 +402,8 @@ func (dao *Dao) saveViewCollection(newCollection, oldCollection *models.Collecti
// currently we don't support non-string model ids // currently we don't support non-string model ids
// (see https://github.com/pocketbase/pocketbase/issues/3110). // (see https://github.com/pocketbase/pocketbase/issues/3110).
func (dao *Dao) normalizeViewQueryId(query string) (string, error) { func (dao *Dao) normalizeViewQueryId(query string) (string, error) {
query = strings.Trim(strings.TrimSpace(query), ";")
parsed, err := dao.parseQueryToFields(query) parsed, err := dao.parseQueryToFields(query)
if err != nil { if err != nil {
return "", err return "", err

View File

@ -43,10 +43,10 @@ func (dao *Dao) SaveView(name string, selectQuery string) error {
return err return err
} }
trimmed := strings.Trim(selectQuery, ";") selectQuery = strings.Trim(strings.TrimSpace(selectQuery), ";")
// try to eagerly detect multiple inline statements // try to eagerly detect multiple inline statements
tk := tokenizer.NewFromString(trimmed) tk := tokenizer.NewFromString(selectQuery)
tk.Separators(';') tk.Separators(';')
if queryParts, _ := tk.ScanAll(); len(queryParts) > 1 { if queryParts, _ := tk.ScanAll(); len(queryParts) > 1 {
return errors.New("multiple statements are not supported") return errors.New("multiple statements are not supported")
@ -56,7 +56,7 @@ func (dao *Dao) SaveView(name string, selectQuery string) error {
// //
// note: the query is wrapped in a secondary SELECT as a rudimentary // note: the query is wrapped in a secondary SELECT as a rudimentary
// measure to discourage multiple inline sql statements execution. // measure to discourage multiple inline sql statements execution.
viewQuery := fmt.Sprintf("CREATE VIEW {{%s}} AS SELECT * FROM (%s)", name, trimmed) viewQuery := fmt.Sprintf("CREATE VIEW {{%s}} AS SELECT * FROM (%s)", name, selectQuery)
if _, err := txDao.DB().NewQuery(viewQuery).Execute(); err != nil { if _, err := txDao.DB().NewQuery(viewQuery).Execute(); err != nil {
return err return err
} }
@ -458,7 +458,7 @@ type identifiersParser struct {
} }
func (p *identifiersParser) parse(selectQuery string) error { func (p *identifiersParser) parse(selectQuery string) error {
str := strings.Trim(selectQuery, ";") str := strings.Trim(strings.TrimSpace(selectQuery), ";")
str = joinReplaceRegex.ReplaceAllString(str, " _join_ ") str = joinReplaceRegex.ReplaceAllString(str, " _join_ ")
str = discardReplaceRegex.ReplaceAllString(str, " _discard_ ") str = discardReplaceRegex.ReplaceAllString(str, " _discard_ ")
str = commentsReplaceRegex.ReplaceAllString(str, "") str = commentsReplaceRegex.ReplaceAllString(str, "")
@ -599,13 +599,20 @@ func identifierFromParts(parts []string) (identifier, error) {
} }
result.original = trimRawIdentifier(result.original) result.original = trimRawIdentifier(result.original)
result.alias = trimRawIdentifier(result.alias)
// we trim the single quote even though it is not a valid column quote character
// because SQLite allows it if the context expects an identifier and not string literal
// (https://www.sqlite.org/lang_keywords.html)
result.alias = trimRawIdentifier(result.alias, "'")
return result, nil return result, nil
} }
func trimRawIdentifier(rawIdentifier string) string { func trimRawIdentifier(rawIdentifier string, extraTrimChars ...string) string {
const trimChars = "`\"[];" trimChars := "`\"[];"
if len(extraTrimChars) > 0 {
trimChars += strings.Join(extraTrimChars, "")
}
parts := strings.Split(rawIdentifier, ".") parts := strings.Split(rawIdentifier, ".")

View File

@ -147,34 +147,33 @@ func TestSaveView(t *testing.T) {
} }
for _, s := range scenarios { for _, s := range scenarios {
err := app.Dao().SaveView(s.viewName, s.query) t.Run(s.scenarioName, func(t *testing.T) {
err := app.Dao().SaveView(s.viewName, s.query)
hasErr := err != nil hasErr := err != nil
if hasErr != s.expectError { if hasErr != s.expectError {
t.Errorf("[%s] Expected hasErr %v, got %v (%v)", s.scenarioName, s.expectError, hasErr, err) t.Fatalf("Expected hasErr %v, got %v (%v)", s.expectError, hasErr, err)
continue
}
if hasErr {
continue
}
infoRows, err := app.Dao().TableInfo(s.viewName)
if err != nil {
t.Errorf("[%s] Failed to fetch table info for %s: %v", s.scenarioName, s.viewName, err)
continue
}
if len(s.expectColumns) != len(infoRows) {
t.Errorf("[%s] Expected %d columns, got %d", s.scenarioName, len(s.expectColumns), len(infoRows))
continue
}
for _, row := range infoRows {
if !list.ExistInSlice(row.Name, s.expectColumns) {
t.Errorf("[%s] Missing %q column in %v", s.scenarioName, row.Name, s.expectColumns)
} }
}
if hasErr {
return
}
infoRows, err := app.Dao().TableInfo(s.viewName)
if err != nil {
t.Fatalf("Failed to fetch table info for %s: %v", s.viewName, err)
}
if len(s.expectColumns) != len(infoRows) {
t.Fatalf("Expected %d columns, got %d", len(s.expectColumns), len(infoRows))
}
for _, row := range infoRows {
if !list.ExistInSlice(row.Name, s.expectColumns) {
t.Fatalf("Missing %q column in %v", row.Name, s.expectColumns)
}
}
})
} }
ensureNoTempViews(app, t) ensureNoTempViews(app, t)
@ -272,24 +271,26 @@ func TestCreateViewSchema(t *testing.T) {
"datetime", "datetime",
"json", "json",
"rel_one", "rel_one",
"rel_many" "rel_many",
'single_quoted_custom_literal' as 'single_quoted_column'
from demo1 from demo1
`, `,
false, false,
map[string]string{ map[string]string{
"text": schema.FieldTypeText, "text": schema.FieldTypeText,
"bool": schema.FieldTypeBool, "bool": schema.FieldTypeBool,
"url": schema.FieldTypeUrl, "url": schema.FieldTypeUrl,
"select_one": schema.FieldTypeSelect, "select_one": schema.FieldTypeSelect,
"select_many": schema.FieldTypeSelect, "select_many": schema.FieldTypeSelect,
"file_one": schema.FieldTypeFile, "file_one": schema.FieldTypeFile,
"file_many": schema.FieldTypeFile, "file_many": schema.FieldTypeFile,
"number_alias": schema.FieldTypeNumber, "number_alias": schema.FieldTypeNumber,
"email": schema.FieldTypeEmail, "email": schema.FieldTypeEmail,
"datetime": schema.FieldTypeDate, "datetime": schema.FieldTypeDate,
"json": schema.FieldTypeJson, "json": schema.FieldTypeJson,
"rel_one": schema.FieldTypeRelation, "rel_one": schema.FieldTypeRelation,
"rel_many": schema.FieldTypeRelation, "rel_many": schema.FieldTypeRelation,
"single_quoted_column": schema.FieldTypeJson,
}, },
}, },
{ {